tstVD.cpp revision b6bf58bbb273b6728171c2a5a662c559acff43ff
/** @file
*
* Simple VBox HDD container test utility.
*/
/*
* Copyright (C) 2006-2007 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* General Public License (GPL) as published by the Free Software
* Foundation, in version 2 as it comes in the "COPYING" file of the
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
#include <iprt/initterm.h>
#include "stdio.h"
#include "stdlib.h"
#define VHD_TEST
#define VDI_TEST
#define VMDK_TEST
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** The error count. */
unsigned g_cErrors = 0;
{
g_cErrors++;
RTPrintf("\n");
}
{
int rc;
PDMMEDIAGEOMETRY PCHS = { 0, 0, 0 };
PDMMEDIAGEOMETRY LCHS = { 0, 0, 0 };
do \
{ \
if (RT_FAILURE(rc)) \
{ \
return rc; \
} \
} while (0)
/* Create error interface. */
CHECK("VDCreate()");
CHECK("VDCreateBase()");
if (fDelete)
{
if (RT_SUCCESS(rc))
{
return VERR_INTERNAL_ERROR;
}
}
return 0;
}
{
int rc;
PDMMEDIAGEOMETRY PCHS = { 0, 0, 0 };
PDMMEDIAGEOMETRY LCHS = { 0, 0, 0 };
do \
{ \
if (RT_FAILURE(rc)) \
{ \
return rc; \
} \
} while (0)
/* Create error interface. */
CHECK("VDCreate()");
CHECK("VDOpen()");
if (RT_SUCCESS(rc))
{
return VERR_INTERNAL_ERROR;
}
return 0;
}
#define RTDECL(x) static x
/* Start of IPRT code */
/**
* The following code is based on the work of George Marsaglia
* taken from
* and
*/
/*
A C version of a very very good 64-bit RNG is given below.
You should be able to adapt it to your particular needs.
It is based on the complimentary-multiple-with-carry
sequence
x(n)=a*x(n-4)+carry mod 2^64-1,
which works as follows:
Assume a certain multiplier 'a' and a base 'b'.
Given a current x value and a current carry 'c',
form: t=a*x+c
Then the new carry is c=floor(t/b)
and the new x value is x = b-1-(t mod b).
Ordinarily, for 32-bit mwc or cmwc sequences, the
value t=a*x+c can be formed in 64 bits, then the new c
is the top and the new x the bottom 32 bits (with a little
fiddling when b=2^32-1 and cmwc rather than mwc.)
To generate 64-bit x's, it is difficult to form
t=a*x+c in 128 bits then get the new c and new x
from the top and bottom halves.
But if 'a' has a special form, for example,
a=2^62+2^47+2 and b=2^64-1, then the new c and
the new x can be formed with shifts, tests and +/-'s,
again with a little fiddling because b=2^64-1 rather
than 2^64. (The latter is not an optimal choice because,
being a square, it cannot be a primitive root of the
prime a*b^k+1, where 'k' is the 'lag':
x(n)=a*x(n-k)+carry mod b.)
But the multiplier a=2^62+2^47+2 makes a*b^4+1 a prime for
which b=2^64-1 is a primitive root, and getting the new x and
new c can be done with arithmetic on integers the size of x.
*/
struct RndCtx
{
uint64_t x;
uint64_t y;
uint64_t z;
uint64_t w;
uint64_t c;
};
/**
* Initialize seeds.
*
* @remarks You should choose ANY 4 random 64-bit
* seeds x,y,z,w < 2^64-1 and a random seed c in
* 0<= c < a = 2^62+2^47+2.
* There are P=(2^62+2^46+2)*(2^64-1)^4 > 2^318 possible choices
* for seeds, the period of the RNG.
*/
{
if (u32Seed == 0)
/* Zero is not a good seed. */
if (u32Seed == 0)
u32Seed = 362436069;
pCtx->y = 17280675555674358941ULL;
pCtx->z = 6376492577913983186ULL;
pCtx->w = 9064188857900113776ULL;
pCtx->c = 123456789;
return VINF_SUCCESS;
}
{
}
/**
* Generate a 64-bit unsigned random number.
*
* @returns The pseudo random number.
*/
{
uint64_t t;
}
/**
* Generate a 64-bit unsigned pseudo random number in the set
* [u64First..u64Last].
*
* @returns The pseudo random number.
* @param u64First First number in the set.
* @param u64Last Last number in the set.
*/
{
return RTPRandU64(pCtx);
do
{
}
/**
* Generate a 32-bit unsigned random number.
*
* @returns The pseudo random number.
*/
{
}
/**
* Generate a 32-bit unsigned pseudo random number in the set
* [u32First..u32Last].
*
* @returns The pseudo random number.
* @param u32First First number in the set.
* @param u32Last Last number in the set.
*/
{
return RTPRandU32(pCtx);
do
{
}
/* End of IPRT code */
struct Segment
{
};
{
if (RT_FAILURE(rc))
else
{
}
}
{
/* Note that no duplicates are allowed in the array being sorted. */
}
static void generateRandomSegments(PRNDCTX pCtx, PSEGMENT pSegment, uint32_t nSegments, uint32_t u32MaxSegmentSize, uint64_t u64DiskSize, uint32_t u32SectorSize, uint8_t u8ValueLow, uint8_t u8ValueHigh)
{
uint32_t i;
/* Generate segment offsets. */
for (i = 0; i < nSegments; i++)
{
bool fDuplicateFound;
do
{
fDuplicateFound = false;
for (uint32_t j = 0; j < i; j++)
{
fDuplicateFound = true;
break;
}
} while (fDuplicateFound);
}
/* Sort in offset-ascending order. */
/* Put a sentinel at the end. */
/* Generate segment lengths and values. */
for (i = 0; i < nSegments; i++)
{
pSegment[i].u32Length = RTPRandU32Ex(pCtx, 1, RT_MIN(pSegment[i+1].u64Offset - pSegment[i].u64Offset,
}
}
static void mergeSegments(PSEGMENT pBaseSegment, PSEGMENT pDiffSegment, PSEGMENT pMergeSegment, uint32_t u32MaxLength)
{
{
{
pBaseSegment++;
else
{
{
pBaseSegment->u32Length -= pDiffSegment->u64Offset + pDiffSegment->u32Length - pBaseSegment->u64Offset;
}
else
pBaseSegment++;
}
}
else
{
{
pDiffSegment++;
}
else
{
if (pBaseSegment->u64Offset + pBaseSegment->u32Length > pDiffSegment->u64Offset + pDiffSegment->u32Length)
{
pBaseSegment->u32Length -= pDiffSegment->u64Offset + pDiffSegment->u32Length - pBaseSegment->u64Offset;
pDiffSegment++;
}
else
pBaseSegment++;
}
}
}
}
{
{
//memset((uint8_t*)pvBuf + pSegment->u64Offset, pSegment->u8Value, pSegment->u32Length);
pSegment++;
}
}
{
{
if (RT_FAILURE(rc))
{
RTPrintf("ERROR: Failed to read from virtual disk\n");
return rc;
}
else
{
{
RTPrintf("ERROR: Segment at %Lx of %x bytes is corrupt at offset %x (found %x instead of %x)\n",
RTLogPrintf("ERROR: Segment at %Lx of %x bytes is corrupt at offset %x (found %x instead of %x)\n",
return VERR_INTERNAL_ERROR;
}
}
pSegment++;
}
return VINF_SUCCESS;
}
static int tstVDOpenCreateWriteMerge(const char *pszBackend,
const char *pszBaseFilename,
const char *pszDiffFilename,
{
int rc;
char *pszFormat;
PDMMEDIAGEOMETRY PCHS = { 0, 0, 0 };
PDMMEDIAGEOMETRY LCHS = { 0, 0, 0 };
do \
{ \
if (RT_FAILURE(rc)) \
{ \
if (pvBuf) \
return rc; \
} \
} while (0)
/* Create error interface. */
CHECK("VDCreate()");
if (RT_SUCCESS(rc))
{
{
RTPrintf("VDGetFormat() returned incorrect backend name\n");
}
CHECK("VDGetFormat()");
NULL);
CHECK("VDOpen()");
}
else
{
VD_IMAGE_FLAGS_NONE, "Test image",
CHECK("VDCreateBase()");
}
int nSegments = 100;
/* Allocate one extra element for a sentinel. */
generateRandomSegments(&ctx, paDiffSegments, nSegments, _1M, u64DiskSize, u32SectorSize, 128u, 255u);
/*PSEGMENT pSegment;
RTPrintf("Base segments:\n");
for (pSegment = paBaseSegments; pSegment->u32Length; pSegment++)
RTPrintf("off: %08Lx len: %05x val: %02x\n", pSegment->u64Offset, pSegment->u32Length, pSegment->u8Value);*/
CHECK("VDCreateDiff()");
/*RTPrintf("\nDiff segments:\n");
for (pSegment = paDiffSegments; pSegment->u32Length; pSegment++)
RTPrintf("off: %08Lx len: %05x val: %02x\n", pSegment->u64Offset, pSegment->u32Length, pSegment->u8Value);*/
RTPrintf("Merging diff into base..\n");
CHECK("VDMerge()");
/*RTPrintf("\nMerged segments:\n");
for (pSegment = paMergeSegments; pSegment->u32Length; pSegment++)
RTPrintf("off: %08Lx len: %05x val: %02x\n", pSegment->u64Offset, pSegment->u32Length, pSegment->u8Value);*/
CHECK("readAndCompareSegments()");
if (pvBuf)
return 0;
}
static int tstVDCreateWriteOpenRead(const char *pszBackend,
const char *pszFilename,
{
int rc;
PDMMEDIAGEOMETRY PCHS = { 0, 0, 0 };
PDMMEDIAGEOMETRY LCHS = { 0, 0, 0 };
do \
{ \
if (RT_FAILURE(rc)) \
{ \
if (pvBuf) \
return rc; \
} \
} while (0)
/* Create error interface. */
CHECK("VDCreate()");
if (RT_SUCCESS(rc))
{
}
VD_IMAGE_FLAGS_NONE, "Test image",
CHECK("VDCreateBase()");
int nSegments = 100;
/* Allocate one extra element for a sentinel. */
/*for (PSEGMENT pSegment = paSegments; pSegment->u32Length; pSegment++)
RTPrintf("off: %08Lx len: %05x val: %02x\n", pSegment->u64Offset, pSegment->u32Length, pSegment->u8Value);*/
CHECK("VDOpen()");
CHECK("readAndCompareSegments()");
if (pvBuf)
return 0;
}
{
int rc;
do \
{ \
if (RT_FAILURE(rc)) \
{ \
return rc; \
} \
} while (0)
/* Create error interface. */
CHECK("VDCreate()");
CHECK("VDOpen()");
CHECK("VDCopy()");
return 0;
}
unsigned uFlags)
{
if (RT_FAILURE(rc))
return rc;
if (RT_FAILURE(rc))
return rc;
do \
{ \
if (RT_FAILURE(rc)) \
{ \
VDCloseAll(pVD); \
return rc; \
} \
} while (0)
/* Create error interface. */
CHECK("VDCreate()");
CHECK("VDOpen()");
CHECK("VDClose()");
return rc;
}
#if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
#define DST_PATH "tmp\\tmpVDRename.vmdk"
#else
#define DST_PATH "tmp/tmpVDRename.vmdk"
#endif
static void tstVmdk()
{
if (RT_FAILURE(rc))
{
RTPrintf("tstVD: VMDK rename (single extent, embedded descriptor, same dir) test failed! rc=%Rrc\n", rc);
g_cErrors++;
}
if (RT_FAILURE(rc))
{
RTPrintf("tstVD: VMDK rename (multiple extent, separate descriptor, same dir) test failed! rc=%Rrc\n", rc);
g_cErrors++;
}
if (RT_FAILURE(rc))
{
RTPrintf("tstVD: VMDK rename (single extent, embedded descriptor, another dir) test failed! rc=%Rrc\n", rc);
g_cErrors++;
}
if (RT_FAILURE(rc))
{
RTPrintf("tstVD: VMDK rename (multiple extent, separate descriptor, another dir) test failed! rc=%Rrc\n", rc);
g_cErrors++;
}
if (RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
{
RTPrintf("tstVD: VMDK rename (multiple extent, separate descriptor, another dir, already exists) test failed!\n");
g_cErrors++;
}
RTFileDelete("tmpVDCreate.vmdk");
RTFileDelete("tmpVDCreate-s001.vmdk");
RTFileDelete("tmpVDCreate-s002.vmdk");
RTFileDelete("tmpVDCreate-s003.vmdk");
}
{
RTR3Init();
int rc;
if (argc > 1)
{
RTPrintf("ERROR: Invalid parameter %s. Valid usage is %s <32-bit seed>.\n",
return 1;
}
RTPrintf("tstVD: TESTING...\n");
/*
* Clean up potential leftovers from previous unsuccessful runs.
*/
RTFileDelete("tmpVDCreate.vdi");
RTFileDelete("tmpVDCreate.vmdk");
RTFileDelete("tmpVDCreate.vhd");
RTFileDelete("tmpVDBase.vdi");
RTFileDelete("tmpVDDiff.vdi");
RTFileDelete("tmpVDBase.vmdk");
RTFileDelete("tmpVDDiff.vmdk");
RTFileDelete("tmpVDBase.vhd");
RTFileDelete("tmpVDDiff.vhd");
RTFileDelete("tmpVDCreate-s001.vmdk");
RTFileDelete("tmpVDCreate-s002.vmdk");
RTFileDelete("tmpVDCreate-s003.vmdk");
RTFileDelete("tmpVDRename.vmdk");
RTFileDelete("tmpVDRename-s001.vmdk");
RTFileDelete("tmpVDRename-s002.vmdk");
RTFileDelete("tmpVDRename-s003.vmdk");
if (!RTDirExists("tmp"))
{
if (RT_FAILURE(rc))
{
g_cErrors++;
}
}
#ifdef VMDK_TEST
true);
if (RT_FAILURE(rc))
{
g_cErrors++;
}
false);
if (RT_FAILURE(rc))
{
g_cErrors++;
}
if (RT_FAILURE(rc))
{
g_cErrors++;
}
tstVmdk();
#endif /* VMDK_TEST */
#ifdef VDI_TEST
true);
if (RT_FAILURE(rc))
{
g_cErrors++;
}
true);
if (RT_FAILURE(rc))
{
g_cErrors++;
}
#endif /* VDI_TEST */
#ifdef VMDK_TEST
true);
if (RT_FAILURE(rc))
{
g_cErrors++;
}
true);
if (RT_FAILURE(rc))
{
g_cErrors++;
}
true);
if (RT_FAILURE(rc))
{
g_cErrors++;
}
true);
if (RT_FAILURE(rc))
{
g_cErrors++;
}
#endif /* VMDK_TEST */
#ifdef VHD_TEST
true);
if (RT_FAILURE(rc))
{
g_cErrors++;
}
true);
if (RT_FAILURE(rc))
{
g_cErrors++;
}
#endif /* VHD_TEST */
#ifdef VDI_TEST
if (RT_FAILURE(rc))
{
g_cErrors++;
}
if (RT_FAILURE(rc))
{
g_cErrors++;
}
#endif /* VDI_TEST */
#ifdef VMDK_TEST
if (RT_FAILURE(rc))
{
g_cErrors++;
}
if (RT_FAILURE(rc))
{
g_cErrors++;
}
#endif /* VMDK_TEST */
#ifdef VHD_TEST
if (RT_FAILURE(rc))
{
g_cErrors++;
}
if (RT_FAILURE(rc))
{
g_cErrors++;
}
#endif /* VHD_TEST */
/*
* Clean up any leftovers.
*/
RTFileDelete("tmpVDCreate.vdi");
RTFileDelete("tmpVDCreate.vmdk");
RTFileDelete("tmpVDCreate.vhd");
RTFileDelete("tmpVDBase.vdi");
RTFileDelete("tmpVDDiff.vdi");
RTFileDelete("tmpVDBase.vmdk");
RTFileDelete("tmpVDDiff.vmdk");
RTFileDelete("tmpVDBase.vhd");
RTFileDelete("tmpVDDiff.vhd");
RTFileDelete("tmpVDCreate-s001.vmdk");
RTFileDelete("tmpVDCreate-s002.vmdk");
RTFileDelete("tmpVDCreate-s003.vmdk");
RTFileDelete("tmpVDRename.vmdk");
RTFileDelete("tmpVDRename-s001.vmdk");
RTFileDelete("tmpVDRename-s002.vmdk");
RTFileDelete("tmpVDRename-s003.vmdk");
rc = VDShutdown();
if (RT_FAILURE(rc))
{
g_cErrors++;
}
/*
* Summary
*/
if (!g_cErrors)
RTPrintf("tstVD: SUCCESS\n");
else
return !!g_cErrors;
}