VirtualBox

Changeset 31193 in vbox


Ignore:
Timestamp:
Jul 29, 2010 8:06:22 AM (14 years ago)
Author:
vboxsync
Message:

Devices/Storage: start of a testcase for shareable images

Location:
trunk/src/VBox/Devices/Storage/testcase
Files:
1 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Storage/testcase/Makefile.kmk

    r28800 r31193  
    55
    66#
    7 # Copyright (C) 2006-2007 Oracle Corporation
     7# Copyright (C) 2006-2010 Oracle Corporation
    88#
    99# This file is part of VirtualBox Open Source Edition (OSE), as
     
    4949#
    5050ifdef VBOX_WITH_TESTCASES
    51  PROGRAMS += tstVD tstVD-2 tstVDCopy tstVDSnap
     51 PROGRAMS += tstVD tstVD-2 tstVDCopy tstVDSnap tstVDShareable
    5252 ifeq ($(KBUILD_TARGET),l4)
    5353  tstVD_TEMPLATE = VBOXLNXHOSTR3EXE
     
    5555  tstVDCopy_TEMPLATE = VBOXLNXHOSTR3EXE
    5656  tstVDSnap_TEMPLATE = VBOXLNXHOSTR3EXE
     57  tstVDShareable_TEMPLATE = VBOXLNXHOSTR3EXE
    5758 else
    5859  tstVD_TEMPLATE = VBOXR3TSTEXE
     
    6061  tstVDCopy_TEMPLATE = VBOXR3TSTEXE
    6162  tstVDSnap_TEMPLATE = VBOXR3TSTEXE
     63  tstVDShareable_TEMPLATE = VBOXR3TSTEXE
    6264 endif
    6365 tstVD_LIBS     = $(LIB_DDU) $(LIB_RUNTIME)
     
    6567 tstVDCopy_LIBS   = $(LIB_DDU) $(LIB_RUNTIME)
    6668 tstVDSnap_LIBS   = $(LIB_DDU) $(LIB_RUNTIME)
     69 tstVDShareable_LIBS   = $(LIB_DDU) $(LIB_RUNTIME)
    6770 tstVD_SOURCES  = tstVD.cpp
    6871 tstVD-2_SOURCES  = tstVD-2.cpp
    6972 tstVDCopy_SOURCES  = tstVDCopy.cpp
    7073 tstVDSnap_SOURCES  = tstVDSnap.cpp
     74 tstVDShareable_SOURCES = tstVDShareable.cpp
    7175endif
    7276
  • trunk/src/VBox/Devices/Storage/testcase/tstVDShareable.cpp

    r31182 r31193  
    11/* $Id$ */
    22/** @file
    3  * Simple VBox HDD container test utility.
     3 * Simple VBox HDD container test utility for shareable images.
    44 */
    55
    66/*
    7  * Copyright (C) 2006-2007 Oracle Corporation
     7 * Copyright (C) 2010 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    6464}
    6565
    66 static int tstVDCreateDelete(const char *pszBackend, const char *pszFilename,
    67                              uint64_t cbSize, unsigned uFlags, bool fDelete)
     66static int tstVDCreateShareDelete(const char *pszBackend, const char *pszFilename,
     67                                  uint64_t cbSize, unsigned uFlags)
    6868{
    6969    int rc;
    70     PVBOXHDD pVD = NULL;
     70    PVBOXHDD pVD = NULL, pVD2 = NULL;
    7171    PDMMEDIAGEOMETRY PCHS = { 0, 0, 0 };
    7272    PDMMEDIAGEOMETRY LCHS = { 0, 0, 0 };
     
    9898    rc = VDCreate(&VDIError, &pVD);
    9999    CHECK("VDCreate()");
     100    rc = VDCreate(&VDIError, &pVD2);
     101    CHECK("VDCreate() #2");
    100102
    101103    rc = VDCreateBase(pVD, pszBackend, pszFilename, cbSize,
     
    104106    CHECK("VDCreateBase()");
    105107
    106     VDDumpImages(pVD);
     108    VDClose(pVD, false);
    107109
    108     VDClose(pVD, fDelete);
    109     if (fDelete)
    110     {
    111         RTFILE File;
    112         rc = RTFileOpen(&File, pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
    113         if (RT_SUCCESS(rc))
    114         {
    115             RTFileClose(File);
    116             return VERR_INTERNAL_ERROR;
    117         }
    118     }
     110    rc = VDOpen(pVD, pszBackend, pszFilename, VD_OPEN_FLAGS_SHAREABLE, NULL);
     111    CHECK("VDOpen()");
     112    rc = VDOpen(pVD2, pszBackend, pszFilename, VD_OPEN_FLAGS_SHAREABLE, NULL);
     113    CHECK("VDOpen() #2");
     114    if (VDIsReadOnly(pVD2))
     115        rc = VERR_VD_IMAGE_READ_ONLY;
     116
     117    VDClose(pVD2, false);
     118    VDClose(pVD, true);
    119119
    120120    VDDestroy(pVD);
     121    VDDestroy(pVD2);
    121122#undef CHECK
    122123    return 0;
    123 }
    124 
    125 static int tstVDOpenDelete(const char *pszBackend, const char *pszFilename)
    126 {
    127     int rc;
    128     PVBOXHDD pVD = NULL;
    129     PDMMEDIAGEOMETRY PCHS = { 0, 0, 0 };
    130     PDMMEDIAGEOMETRY LCHS = { 0, 0, 0 };
    131     PVDINTERFACE     pVDIfs = NULL;
    132     VDINTERFACE      VDIError;
    133     VDINTERFACEERROR VDIErrorCallbacks;
    134 
    135 #define CHECK(str) \
    136     do \
    137     { \
    138         RTPrintf("%s rc=%Rrc\n", str, rc); \
    139         if (RT_FAILURE(rc)) \
    140         { \
    141             VDDestroy(pVD); \
    142             return rc; \
    143         } \
    144     } while (0)
    145 
    146     /* Create error interface. */
    147     VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
    148     VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
    149     VDIErrorCallbacks.pfnError = tstVDError;
    150     VDIErrorCallbacks.pfnMessage = tstVDMessage;
    151 
    152     rc = VDInterfaceAdd(&VDIError, "tstVD_Error", VDINTERFACETYPE_ERROR, &VDIErrorCallbacks,
    153                         NULL, &pVDIfs);
    154     AssertRC(rc);
    155 
    156     rc = VDCreate(&VDIError, &pVD);
    157     CHECK("VDCreate()");
    158 
    159     rc = VDOpen(pVD, pszBackend, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
    160     CHECK("VDOpen()");
    161 
    162     VDDumpImages(pVD);
    163 
    164     VDClose(pVD, true);
    165     RTFILE File;
    166     rc = RTFileOpen(&File, pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
    167     if (RT_SUCCESS(rc))
    168     {
    169         RTFileClose(File);
    170         return VERR_INTERNAL_ERROR;
    171     }
    172 
    173     VDDestroy(pVD);
    174 #undef CHECK
    175     return 0;
    176 }
    177 
    178 
    179 #undef RTDECL
    180 #define RTDECL(x) static x
    181 
    182 /* Start of IPRT code */
    183 
    184 /**
    185  * The following code is based on the work of George Marsaglia
    186  * taken from
    187  * http://groups.google.ws/group/comp.sys.sun.admin/msg/7c667186f6cbf354
    188  * and
    189  * http://groups.google.ws/group/comp.lang.c/msg/0e170777c6e79e8d
    190  */
    191 
    192 /*
    193 A C version of a very very good 64-bit RNG is given below.
    194 You should be able to adapt it to your particular needs.
    195 
    196 It is based on the complimentary-multiple-with-carry
    197 sequence
    198          x(n)=a*x(n-4)+carry mod 2^64-1,
    199 which works as follows:
    200 Assume a certain multiplier 'a' and a base 'b'.
    201 Given a current x value and a current carry 'c',
    202 form:               t=a*x+c
    203 Then the new carry is     c=floor(t/b)
    204 and the new x value is    x = b-1-(t mod b).
    205 
    206 
    207 Ordinarily, for 32-bit mwc or cmwc sequences, the
    208 value t=a*x+c can be formed in 64 bits, then the new c
    209 is the top and the new x the bottom 32 bits (with a little
    210 fiddling when b=2^32-1 and cmwc rather than mwc.)
    211 
    212 
    213 To generate 64-bit x's, it is difficult to form
    214 t=a*x+c in 128 bits then get the new c and new x
    215 from the top and bottom halves.
    216 But if 'a' has a special form, for example,
    217 a=2^62+2^47+2 and b=2^64-1, then the new c and
    218 the new x can be formed with shifts, tests and +/-'s,
    219 again with a little fiddling because b=2^64-1 rather
    220 than 2^64.   (The latter is not an optimal choice because,
    221 being a square, it cannot be a primitive root of the
    222 prime a*b^k+1, where 'k' is the 'lag':
    223         x(n)=a*x(n-k)+carry mod b.)
    224 But the multiplier a=2^62+2^47+2 makes a*b^4+1 a prime for
    225 which b=2^64-1 is a primitive root, and getting  the new x and
    226 new c  can be done with arithmetic on integers the size of x.
    227 */
    228 
    229 struct RndCtx
    230 {
    231     uint64_t x;
    232     uint64_t y;
    233     uint64_t z;
    234     uint64_t w;
    235     uint64_t c;
    236     uint32_t u32x;
    237     uint32_t u32y;
    238 };
    239 typedef struct RndCtx RNDCTX;
    240 typedef RNDCTX *PRNDCTX;
    241 
    242 /**
    243  * Initialize seeds.
    244  *
    245  * @remarks You should choose ANY 4 random 64-bit
    246  * seeds x,y,z,w < 2^64-1 and a random seed c in
    247  * 0<= c < a = 2^62+2^47+2.
    248  * There are P=(2^62+2^46+2)*(2^64-1)^4 > 2^318 possible choices
    249  * for seeds, the period of the RNG.
    250  */
    251 RTDECL(int) RTPRandInit(PRNDCTX pCtx, uint32_t u32Seed)
    252 {
    253     if (u32Seed == 0)
    254         u32Seed = (uint32_t)(ASMReadTSC() >> 8);
    255     /* Zero is not a good seed. */
    256     if (u32Seed == 0)
    257         u32Seed = 362436069;
    258     pCtx->x = u32Seed;
    259     pCtx->y = 17280675555674358941ULL;
    260     pCtx->z = 6376492577913983186ULL;
    261     pCtx->w = 9064188857900113776ULL;
    262     pCtx->c = 123456789;
    263     pCtx->u32x = 2282008;
    264     pCtx->u32y = u32Seed;
    265     return VINF_SUCCESS;
    266 }
    267 
    268 RTDECL(uint32_t) RTPRandGetSeedInfo(PRNDCTX pCtx)
    269 {
    270     return pCtx->u32y;
    271 }
    272 
    273 /**
    274  * Generate a 64-bit unsigned random number.
    275  *
    276  * @returns The pseudo random number.
    277  */
    278 RTDECL(uint64_t) RTPRandU64(PRNDCTX pCtx)
    279 {
    280     uint64_t t;
    281     t = (pCtx->x<<47) + (pCtx->x<<62) + (pCtx->x<<1);
    282     t += pCtx->c; t+= (t < pCtx->c);
    283     pCtx->c = (t<pCtx->c) + (pCtx->x>>17) + (pCtx->x>>2) + (pCtx->x>>63);
    284     pCtx->x = pCtx->y;  pCtx->y = pCtx->z ; pCtx->z = pCtx->w;
    285     return (pCtx->w = ~(t + pCtx->c)-1);
    286 }
    287 
    288 /**
    289  * Generate a 64-bit unsigned pseudo random number in the set
    290  * [u64First..u64Last].
    291  *
    292  * @returns The pseudo random number.
    293  * @param   u64First    First number in the set.
    294  * @param   u64Last     Last number in the set.
    295  */
    296 RTDECL(uint64_t) RTPRandU64Ex(PRNDCTX pCtx, uint64_t u64First, uint64_t u64Last)
    297 {
    298     if (u64First == 0 && u64Last == UINT64_MAX)
    299         return RTPRandU64(pCtx);
    300 
    301     uint64_t u64Tmp;
    302     uint64_t u64Range = u64Last - u64First + 1;
    303     uint64_t u64Scale = UINT64_MAX / u64Range;
    304 
    305     do
    306     {
    307         u64Tmp = RTPRandU64(pCtx) / u64Scale;
    308     } while (u64Tmp >= u64Range);
    309     return u64First + u64Tmp;
    310 }
    311 
    312 /**
    313  * Generate a 32-bit unsigned random number.
    314  *
    315  * @returns The pseudo random number.
    316  */
    317 RTDECL(uint32_t) RTPRandU32(PRNDCTX pCtx)
    318 {
    319     return ( pCtx->u32x = 69069 * pCtx->u32x + 123,
    320              pCtx->u32y ^= pCtx->u32y<<13,
    321              pCtx->u32y ^= pCtx->u32y>>17,
    322              pCtx->u32y ^= pCtx->u32y<<5,
    323              pCtx->u32x + pCtx->u32y );
    324 }
    325 
    326 /**
    327  * Generate a 32-bit unsigned pseudo random number in the set
    328  * [u32First..u32Last].
    329  *
    330  * @returns The pseudo random number.
    331  * @param   u32First    First number in the set.
    332  * @param   u32Last     Last number in the set.
    333  */
    334 RTDECL(uint32_t) RTPRandU32Ex(PRNDCTX pCtx, uint32_t u32First, uint32_t u32Last)
    335 {
    336     if (u32First == 0 && u32Last == UINT32_MAX)
    337         return RTPRandU32(pCtx);
    338 
    339     uint32_t u32Tmp;
    340     uint32_t u32Range = u32Last - u32First + 1;
    341     uint32_t u32Scale = UINT32_MAX / u32Range;
    342 
    343     do
    344     {
    345         u32Tmp = RTPRandU32(pCtx) / u32Scale;
    346     } while (u32Tmp >= u32Range);
    347     return u32First + u32Tmp;
    348 }
    349 
    350 /* End of IPRT code */
    351 
    352 struct Segment
    353 {
    354     uint64_t u64Offset;
    355     uint32_t u32Length;
    356     uint32_t u8Value;
    357 };
    358 typedef struct Segment *PSEGMENT;
    359 
    360 static void initializeRandomGenerator(PRNDCTX pCtx, uint32_t u32Seed)
    361 {
    362     int rc = RTPRandInit(pCtx, u32Seed);
    363     if (RT_FAILURE(rc))
    364         RTPrintf("ERROR: Failed to initialize random generator. RC=%Rrc\n", rc);
    365     else
    366     {
    367         RTPrintf("INFO: Random generator seed used: %x\n", RTPRandGetSeedInfo(pCtx));
    368         RTLogPrintf("INFO: Random generator seed used: %x\n", RTPRandGetSeedInfo(pCtx));
    369     }
    370 }
    371 
    372 static int compareSegments(const void *left, const void *right)
    373 {
    374     /* Note that no duplicates are allowed in the array being sorted. */
    375     return ((PSEGMENT)left)->u64Offset < ((PSEGMENT)right)->u64Offset ? -1 : 1;
    376 }
    377 
    378 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)
    379 {
    380     uint32_t i;
    381     /* Generate segment offsets. */
    382     for (i = 0; i < nSegments; i++)
    383     {
    384         bool fDuplicateFound;
    385         do
    386         {
    387             pSegment[i].u64Offset = RTPRandU64Ex(pCtx, 0, u64DiskSize / u32SectorSize - 1) * u32SectorSize;
    388             fDuplicateFound = false;
    389             for (uint32_t j = 0; j < i; j++)
    390                 if (pSegment[i].u64Offset == pSegment[j].u64Offset)
    391                 {
    392                     fDuplicateFound = true;
    393                     break;
    394                 }
    395         } while (fDuplicateFound);
    396     }
    397     /* Sort in offset-ascending order. */
    398     qsort(pSegment, nSegments, sizeof(*pSegment), compareSegments);
    399     /* Put a sentinel at the end. */
    400     pSegment[nSegments].u64Offset = u64DiskSize;
    401     pSegment[nSegments].u32Length = 0;
    402     /* Generate segment lengths and values. */
    403     for (i = 0; i < nSegments; i++)
    404     {
    405         pSegment[i].u32Length = RTPRandU32Ex(pCtx, 1, RT_MIN(pSegment[i+1].u64Offset - pSegment[i].u64Offset,
    406                                                              u32MaxSegmentSize) / u32SectorSize) * u32SectorSize;
    407         Assert(pSegment[i].u32Length <= u32MaxSegmentSize);
    408         pSegment[i].u8Value  = RTPRandU32Ex(pCtx, (uint32_t)u8ValueLow, (uint32_t)u8ValueHigh);
    409     }
    410 }
    411 
    412 static void mergeSegments(PSEGMENT pBaseSegment, PSEGMENT pDiffSegment, PSEGMENT pMergeSegment, uint32_t 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 
    462 static void writeSegmentsToDisk(PVBOXHDD 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 
    473 static int readAndCompareSegments(PVBOXHDD 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 
    503 static int tstVDOpenCreateWriteMerge(const char *pszBackend,
    504                                      const char *pszBaseFilename,
    505                                      const char *pszDiffFilename,
    506                                      uint32_t u32Seed)
    507 {
    508     int rc;
    509     PVBOXHDD pVD = NULL;
    510     char *pszFormat;
    511     PDMMEDIAGEOMETRY PCHS = { 0, 0, 0 };
    512     PDMMEDIAGEOMETRY LCHS = { 0, 0, 0 };
    513     uint64_t u64DiskSize  = 1000 * _1M;
    514     uint32_t u32SectorSize = 512;
    515     PVDINTERFACE     pVDIfs = NULL;
    516     VDINTERFACE      VDIError;
    517     VDINTERFACEERROR VDIErrorCallbacks;
    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     VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
    536     VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
    537     VDIErrorCallbacks.pfnError = tstVDError;
    538     VDIErrorCallbacks.pfnMessage = tstVDMessage;
    539 
    540     rc = VDInterfaceAdd(&VDIError, "tstVD_Error", VDINTERFACETYPE_ERROR, &VDIErrorCallbacks,
    541                         NULL, &pVDIfs);
    542     AssertRC(rc);
    543 
    544 
    545     rc = VDCreate(&VDIError, &pVD);
    546     CHECK("VDCreate()");
    547 
    548     RTFILE File;
    549     rc = RTFileOpen(&File, pszBaseFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
    550     if (RT_SUCCESS(rc))
    551     {
    552         RTFileClose(File);
    553         rc = VDGetFormat(NULL, pszBaseFilename, &pszFormat);
    554         RTPrintf("VDGetFormat() pszFormat=%s rc=%Rrc\n", pszFormat, rc);
    555         if (RT_SUCCESS(rc) && strcmp(pszFormat, pszBackend))
    556         {
    557             rc = VERR_GENERAL_FAILURE;
    558             RTPrintf("VDGetFormat() returned incorrect backend name\n");
    559         }
    560         RTStrFree(pszFormat);
    561         CHECK("VDGetFormat()");
    562 
    563         rc = VDOpen(pVD, pszBackend, pszBaseFilename, VD_OPEN_FLAGS_NORMAL,
    564                     NULL);
    565         CHECK("VDOpen()");
    566     }
    567     else
    568     {
    569         rc = VDCreateBase(pVD, pszBackend, pszBaseFilename, u64DiskSize,
    570                           VD_IMAGE_FLAGS_NONE, "Test image",
    571                           &PCHS, &LCHS, NULL, VD_OPEN_FLAGS_NORMAL,
    572                           NULL, NULL);
    573         CHECK("VDCreateBase()");
    574     }
    575 
    576     int nSegments = 100;
    577     /* Allocate one extra element for a sentinel. */
    578     PSEGMENT paBaseSegments  = (PSEGMENT)RTMemAllocZ(sizeof(struct Segment) * (nSegments + 1));
    579     PSEGMENT paDiffSegments  = (PSEGMENT)RTMemAllocZ(sizeof(struct Segment) * (nSegments + 1));
    580     PSEGMENT paMergeSegments = (PSEGMENT)RTMemAllocZ(sizeof(struct Segment) * (nSegments + 1) * 3);
    581 
    582     RNDCTX ctx;
    583     initializeRandomGenerator(&ctx, u32Seed);
    584     generateRandomSegments(&ctx, paBaseSegments, nSegments, _1M, u64DiskSize, u32SectorSize, 0u, 127u);
    585     generateRandomSegments(&ctx, paDiffSegments, nSegments, _1M, u64DiskSize, u32SectorSize, 128u, 255u);
    586 
    587     /*PSEGMENT pSegment;
    588     RTPrintf("Base segments:\n");
    589     for (pSegment = paBaseSegments; pSegment->u32Length; pSegment++)
    590         RTPrintf("off: %08Lx len: %05x val: %02x\n", pSegment->u64Offset, pSegment->u32Length, pSegment->u8Value);*/
    591     writeSegmentsToDisk(pVD, pvBuf, paBaseSegments);
    592 
    593     rc = VDCreateDiff(pVD, pszBackend, pszDiffFilename,
    594                       VD_IMAGE_FLAGS_NONE, "Test diff image", NULL, NULL,
    595                       VD_OPEN_FLAGS_NORMAL, NULL, NULL);
    596     CHECK("VDCreateDiff()");
    597 
    598     /*RTPrintf("\nDiff segments:\n");
    599     for (pSegment = paDiffSegments; pSegment->u32Length; pSegment++)
    600         RTPrintf("off: %08Lx len: %05x val: %02x\n", pSegment->u64Offset, pSegment->u32Length, pSegment->u8Value);*/
    601     writeSegmentsToDisk(pVD, pvBuf, paDiffSegments);
    602 
    603     VDDumpImages(pVD);
    604 
    605     RTPrintf("Merging diff into base..\n");
    606     rc = VDMerge(pVD, VD_LAST_IMAGE, 0, NULL);
    607     CHECK("VDMerge()");
    608 
    609     mergeSegments(paBaseSegments, paDiffSegments, paMergeSegments, _1M);
    610     /*RTPrintf("\nMerged segments:\n");
    611     for (pSegment = paMergeSegments; pSegment->u32Length; pSegment++)
    612         RTPrintf("off: %08Lx len: %05x val: %02x\n", pSegment->u64Offset, pSegment->u32Length, pSegment->u8Value);*/
    613     rc = readAndCompareSegments(pVD, pvBuf, paMergeSegments);
    614     CHECK("readAndCompareSegments()");
    615 
    616     RTMemFree(paMergeSegments);
    617     RTMemFree(paDiffSegments);
    618     RTMemFree(paBaseSegments);
    619 
    620     VDDumpImages(pVD);
    621 
    622     VDDestroy(pVD);
    623     if (pvBuf)
    624         RTMemFree(pvBuf);
    625 #undef CHECK
    626     return 0;
    627 }
    628 
    629 static int tstVDCreateWriteOpenRead(const char *pszBackend,
    630                                     const char *pszFilename,
    631                                     uint32_t u32Seed)
    632 {
    633     int rc;
    634     PVBOXHDD pVD = NULL;
    635     PDMMEDIAGEOMETRY PCHS = { 0, 0, 0 };
    636     PDMMEDIAGEOMETRY LCHS = { 0, 0, 0 };
    637     uint64_t u64DiskSize  = 1000 * _1M;
    638     uint32_t u32SectorSize = 512;
    639     PVDINTERFACE     pVDIfs = NULL;
    640     VDINTERFACE      VDIError;
    641     VDINTERFACEERROR VDIErrorCallbacks;
    642 
    643 #define CHECK(str) \
    644     do \
    645     { \
    646         RTPrintf("%s rc=%Rrc\n", str, rc); \
    647         if (RT_FAILURE(rc)) \
    648         { \
    649             if (pvBuf) \
    650                 RTMemFree(pvBuf); \
    651             VDDestroy(pVD); \
    652             return rc; \
    653         } \
    654     } while (0)
    655 
    656     void *pvBuf = RTMemAlloc(_1M);
    657 
    658     /* Create error interface. */
    659     VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
    660     VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
    661     VDIErrorCallbacks.pfnError = tstVDError;
    662     VDIErrorCallbacks.pfnMessage = tstVDMessage;
    663 
    664     rc = VDInterfaceAdd(&VDIError, "tstVD_Error", VDINTERFACETYPE_ERROR, &VDIErrorCallbacks,
    665                         NULL, &pVDIfs);
    666     AssertRC(rc);
    667 
    668 
    669     rc = VDCreate(&VDIError, &pVD);
    670     CHECK("VDCreate()");
    671 
    672     RTFILE File;
    673     rc = RTFileOpen(&File, pszFilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
    674     if (RT_SUCCESS(rc))
    675     {
    676         RTFileClose(File);
    677         RTFileDelete(pszFilename);
    678     }
    679 
    680     rc = VDCreateBase(pVD, pszBackend, pszFilename, u64DiskSize,
    681                       VD_IMAGE_FLAGS_NONE, "Test image",
    682                       &PCHS, &LCHS, NULL, VD_OPEN_FLAGS_NORMAL,
    683                       NULL, NULL);
    684     CHECK("VDCreateBase()");
    685 
    686     int nSegments = 100;
    687     /* Allocate one extra element for a sentinel. */
    688     PSEGMENT paSegments  = (PSEGMENT)RTMemAllocZ(sizeof(struct Segment) * (nSegments + 1));
    689 
    690     RNDCTX ctx;
    691     initializeRandomGenerator(&ctx, u32Seed);
    692     generateRandomSegments(&ctx, paSegments, nSegments, _1M, u64DiskSize, u32SectorSize, 0u, 127u);
    693     /*for (PSEGMENT pSegment = paSegments; pSegment->u32Length; pSegment++)
    694         RTPrintf("off: %08Lx len: %05x val: %02x\n", pSegment->u64Offset, pSegment->u32Length, pSegment->u8Value);*/
    695 
    696     writeSegmentsToDisk(pVD, pvBuf, paSegments);
    697 
    698     VDCloseAll(pVD);
    699 
    700     rc = VDOpen(pVD, pszBackend, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
    701     CHECK("VDOpen()");
    702     rc = readAndCompareSegments(pVD, pvBuf, paSegments);
    703     CHECK("readAndCompareSegments()");
    704 
    705     RTMemFree(paSegments);
    706 
    707     VDDestroy(pVD);
    708     if (pvBuf)
    709         RTMemFree(pvBuf);
    710 #undef CHECK
    711     return 0;
    712 }
    713 
    714 static int tstVmdkRename(const char *src, const char *dst)
    715 {
    716     int rc;
    717     PVBOXHDD pVD = NULL;
    718     PVDINTERFACE     pVDIfs = NULL;
    719     VDINTERFACE      VDIError;
    720     VDINTERFACEERROR VDIErrorCallbacks;
    721 
    722 #define CHECK(str) \
    723     do \
    724     { \
    725         RTPrintf("%s rc=%Rrc\n", str, rc); \
    726         if (RT_FAILURE(rc)) \
    727         { \
    728             VDDestroy(pVD); \
    729             return rc; \
    730         } \
    731     } while (0)
    732 
    733     /* Create error interface. */
    734     VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
    735     VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
    736     VDIErrorCallbacks.pfnError = tstVDError;
    737     VDIErrorCallbacks.pfnMessage = tstVDMessage;
    738 
    739     rc = VDInterfaceAdd(&VDIError, "tstVD_Error", VDINTERFACETYPE_ERROR, &VDIErrorCallbacks,
    740                         NULL, &pVDIfs);
    741     AssertRC(rc);
    742 
    743     rc = VDCreate(&VDIError, &pVD);
    744     CHECK("VDCreate()");
    745 
    746     rc = VDOpen(pVD, "VMDK", src, VD_OPEN_FLAGS_NORMAL, NULL);
    747     CHECK("VDOpen()");
    748     rc = VDCopy(pVD, 0, pVD, "VMDK", dst, true, 0, VD_IMAGE_FLAGS_NONE, NULL, NULL, NULL, NULL);
    749     CHECK("VDCopy()");
    750 
    751     VDDestroy(pVD);
    752 #undef CHECK
    753     return 0;
    754 }
    755 
    756 static int tstVmdkCreateRenameOpen(const char *src, const char *dst,
    757                                    uint64_t cbSize, unsigned uFlags)
    758 {
    759     int rc = tstVDCreateDelete("VMDK", src, cbSize, uFlags, false);
    760     if (RT_FAILURE(rc))
    761         return rc;
    762 
    763     rc = tstVmdkRename(src, dst);
    764     if (RT_FAILURE(rc))
    765         return rc;
    766 
    767     PVBOXHDD pVD = NULL;
    768     PVDINTERFACE     pVDIfs = NULL;
    769     VDINTERFACE      VDIError;
    770     VDINTERFACEERROR VDIErrorCallbacks;
    771 
    772 #define CHECK(str) \
    773     do \
    774     { \
    775         RTPrintf("%s rc=%Rrc\n", str, rc); \
    776         if (RT_FAILURE(rc)) \
    777         { \
    778             VDCloseAll(pVD); \
    779             return rc; \
    780         } \
    781     } while (0)
    782 
    783     /* Create error interface. */
    784     VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
    785     VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
    786     VDIErrorCallbacks.pfnError = tstVDError;
    787     VDIErrorCallbacks.pfnMessage = tstVDMessage;
    788 
    789     rc = VDInterfaceAdd(&VDIError, "tstVD_Error", VDINTERFACETYPE_ERROR, &VDIErrorCallbacks,
    790                         NULL, &pVDIfs);
    791     AssertRC(rc);
    792 
    793     rc = VDCreate(&VDIError, &pVD);
    794     CHECK("VDCreate()");
    795 
    796     rc = VDOpen(pVD, "VMDK", dst, VD_OPEN_FLAGS_NORMAL, NULL);
    797     CHECK("VDOpen()");
    798 
    799     VDClose(pVD, true);
    800     CHECK("VDClose()");
    801     VDDestroy(pVD);
    802 #undef CHECK
    803     return rc;
    804 }
    805 
    806 #if defined(RT_OS_OS2) || defined(RT_OS_WINDOWS)
    807 #define DST_PATH "tmp\\tmpVDRename.vmdk"
    808 #else
    809 #define DST_PATH "tmp/tmpVDRename.vmdk"
    810 #endif
    811 
    812 static void tstVmdk()
    813 {
    814     int rc = tstVmdkCreateRenameOpen("tmpVDCreate.vmdk", "tmpVDRename.vmdk", _4G,
    815                                      VD_IMAGE_FLAGS_NONE);
    816     if (RT_FAILURE(rc))
    817     {
    818         RTPrintf("tstVD: VMDK rename (single extent, embedded descriptor, same dir) test failed! rc=%Rrc\n", rc);
    819         g_cErrors++;
    820     }
    821     rc = tstVmdkCreateRenameOpen("tmpVDCreate.vmdk", "tmpVDRename.vmdk", _4G,
    822                                  VD_VMDK_IMAGE_FLAGS_SPLIT_2G);
    823     if (RT_FAILURE(rc))
    824     {
    825         RTPrintf("tstVD: VMDK rename (multiple extent, separate descriptor, same dir) test failed! rc=%Rrc\n", rc);
    826         g_cErrors++;
    827     }
    828     rc = tstVmdkCreateRenameOpen("tmpVDCreate.vmdk", DST_PATH, _4G,
    829                                  VD_IMAGE_FLAGS_NONE);
    830     if (RT_FAILURE(rc))
    831     {
    832         RTPrintf("tstVD: VMDK rename (single extent, embedded descriptor, another dir) test failed! rc=%Rrc\n", rc);
    833         g_cErrors++;
    834     }
    835     rc = tstVmdkCreateRenameOpen("tmpVDCreate.vmdk", DST_PATH, _4G,
    836                                  VD_VMDK_IMAGE_FLAGS_SPLIT_2G);
    837     if (RT_FAILURE(rc))
    838     {
    839         RTPrintf("tstVD: VMDK rename (multiple extent, separate descriptor, another dir) test failed! rc=%Rrc\n", rc);
    840         g_cErrors++;
    841     }
    842 
    843     RTFILE File;
    844     rc = RTFileOpen(&File, DST_PATH, RTFILE_O_WRITE | RTFILE_O_CREATE | RTFILE_O_DENY_NONE);
    845     if (RT_SUCCESS(rc))
    846         RTFileClose(File);
    847 
    848     rc = tstVmdkCreateRenameOpen("tmpVDCreate.vmdk", DST_PATH, _4G,
    849                                  VD_VMDK_IMAGE_FLAGS_SPLIT_2G);
    850     if (RT_SUCCESS(rc))
    851     {
    852         RTPrintf("tstVD: VMDK rename (multiple extent, separate descriptor, another dir, already exists) test failed!\n");
    853         g_cErrors++;
    854     }
    855     RTFileDelete(DST_PATH);
    856     RTFileDelete("tmpVDCreate.vmdk");
    857     RTFileDelete("tmpVDCreate-s001.vmdk");
    858     RTFileDelete("tmpVDCreate-s002.vmdk");
    859     RTFileDelete("tmpVDCreate-s003.vmdk");
    860124}
    861125
     
    865129    int rc;
    866130
    867     uint32_t u32Seed = 0; // Means choose random
    868 
    869     if (argc > 1)
    870         if (sscanf(argv[1], "%x", &u32Seed) != 1)
    871         {
    872             RTPrintf("ERROR: Invalid parameter %s. Valid usage is %s <32-bit seed>.\n",
    873                      argv[1], argv[0]);
    874             return 1;
    875         }
    876 
    877131    RTPrintf("tstVD: TESTING...\n");
    878132
     
    881135     */
    882136    RTFileDelete("tmpVDCreate.vdi");
    883     RTFileDelete("tmpVDCreate.vmdk");
    884     RTFileDelete("tmpVDCreate.vhd");
    885     RTFileDelete("tmpVDBase.vdi");
    886     RTFileDelete("tmpVDDiff.vdi");
    887     RTFileDelete("tmpVDBase.vmdk");
    888     RTFileDelete("tmpVDDiff.vmdk");
    889     RTFileDelete("tmpVDBase.vhd");
    890     RTFileDelete("tmpVDDiff.vhd");
    891     RTFileDelete("tmpVDCreate-s001.vmdk");
    892     RTFileDelete("tmpVDCreate-s002.vmdk");
    893     RTFileDelete("tmpVDCreate-s003.vmdk");
    894     RTFileDelete("tmpVDRename.vmdk");
    895     RTFileDelete("tmpVDRename-s001.vmdk");
    896     RTFileDelete("tmpVDRename-s002.vmdk");
    897     RTFileDelete("tmpVDRename-s003.vmdk");
    898     RTFileDelete("tmp/tmpVDRename.vmdk");
    899     RTFileDelete("tmp/tmpVDRename-s001.vmdk");
    900     RTFileDelete("tmp/tmpVDRename-s002.vmdk");
    901     RTFileDelete("tmp/tmpVDRename-s003.vmdk");
    902137
    903138    if (!RTDirExists("tmp"))
     
    911146    }
    912147
    913 #ifdef VMDK_TEST
    914     rc = tstVDCreateDelete("VMDK", "tmpVDCreate.vmdk", 2 * _4G,
    915                            VD_IMAGE_FLAGS_NONE, true);
     148#ifdef VDI_TEST
     149    rc = tstVDCreateShareDelete("VDI", "tmpVDCreate.vdi", 10 * _1M,
     150                                VD_IMAGE_FLAGS_FIXED);
    916151    if (RT_FAILURE(rc))
    917152    {
    918         RTPrintf("tstVD: dynamic VMDK create test failed! rc=%Rrc\n", rc);
    919         g_cErrors++;
    920     }
    921     rc = tstVDCreateDelete("VMDK", "tmpVDCreate.vmdk", 2 * _4G,
    922                            VD_IMAGE_FLAGS_NONE, false);
    923     if (RT_FAILURE(rc))
    924     {
    925         RTPrintf("tstVD: dynamic VMDK create test failed! rc=%Rrc\n", rc);
    926         g_cErrors++;
    927     }
    928     rc = tstVDOpenDelete("VMDK", "tmpVDCreate.vmdk");
    929     if (RT_FAILURE(rc))
    930     {
    931         RTPrintf("tstVD: VMDK delete test failed! rc=%Rrc\n", rc);
    932         g_cErrors++;
    933     }
    934 
    935     tstVmdk();
    936 #endif /* VMDK_TEST */
    937 #ifdef VDI_TEST
    938     rc = tstVDCreateDelete("VDI", "tmpVDCreate.vdi", 2 * _4G,
    939                            VD_IMAGE_FLAGS_NONE, true);
    940     if (RT_FAILURE(rc))
    941     {
    942         RTPrintf("tstVD: dynamic VDI create test failed! rc=%Rrc\n", rc);
    943         g_cErrors++;
    944     }
    945     rc = tstVDCreateDelete("VDI", "tmpVDCreate.vdi", 2 * _4G,
    946                            VD_IMAGE_FLAGS_NONE, true);
    947     if (RT_FAILURE(rc))
    948     {
    949         RTPrintf("tstVD: fixed VDI create test failed! rc=%Rrc\n", rc);
     153        RTPrintf("tstVD: VDI shareable test failed! rc=%Rrc\n", rc);
    950154        g_cErrors++;
    951155    }
    952156#endif /* VDI_TEST */
    953 #ifdef VMDK_TEST
    954     rc = tstVDCreateDelete("VMDK", "tmpVDCreate.vmdk", 2 * _4G,
    955                            VD_IMAGE_FLAGS_NONE, true);
    956     if (RT_FAILURE(rc))
    957     {
    958         RTPrintf("tstVD: dynamic VMDK create test failed! rc=%Rrc\n", rc);
    959         g_cErrors++;
    960     }
    961     rc = tstVDCreateDelete("VMDK", "tmpVDCreate.vmdk", 2 * _4G,
    962                            VD_VMDK_IMAGE_FLAGS_SPLIT_2G, true);
    963     if (RT_FAILURE(rc))
    964     {
    965         RTPrintf("tstVD: dynamic split VMDK create test failed! rc=%Rrc\n", rc);
    966         g_cErrors++;
    967     }
    968     rc = tstVDCreateDelete("VMDK", "tmpVDCreate.vmdk", 2 * _4G,
    969                            VD_IMAGE_FLAGS_FIXED, true);
    970     if (RT_FAILURE(rc))
    971     {
    972         RTPrintf("tstVD: fixed VMDK create test failed! rc=%Rrc\n", rc);
    973         g_cErrors++;
    974     }
    975     rc = tstVDCreateDelete("VMDK", "tmpVDCreate.vmdk", 2 * _4G,
    976                            VD_IMAGE_FLAGS_FIXED | VD_VMDK_IMAGE_FLAGS_SPLIT_2G,
    977                            true);
    978     if (RT_FAILURE(rc))
    979     {
    980         RTPrintf("tstVD: fixed split VMDK create test failed! rc=%Rrc\n", rc);
    981         g_cErrors++;
    982     }
    983 #endif /* VMDK_TEST */
    984 #ifdef VHD_TEST
    985     rc = tstVDCreateDelete("VHD", "tmpVDCreate.vhd", 2 * _4G,
    986                            VD_IMAGE_FLAGS_NONE, true);
    987     if (RT_FAILURE(rc))
    988     {
    989         RTPrintf("tstVD: dynamic VHD create test failed! rc=%Rrc\n", rc);
    990         g_cErrors++;
    991     }
    992     rc = tstVDCreateDelete("VHD", "tmpVDCreate.vhd", 2 * _4G,
    993                            VD_IMAGE_FLAGS_FIXED, true);
    994     if (RT_FAILURE(rc))
    995     {
    996         RTPrintf("tstVD: fixed VHD create test failed! rc=%Rrc\n", rc);
    997         g_cErrors++;
    998     }
    999 #endif /* VHD_TEST */
    1000 #ifdef VDI_TEST
    1001     rc = tstVDOpenCreateWriteMerge("VDI", "tmpVDBase.vdi", "tmpVDDiff.vdi", u32Seed);
    1002     if (RT_FAILURE(rc))
    1003     {
    1004         RTPrintf("tstVD: VDI test failed (new image)! rc=%Rrc\n", rc);
    1005         g_cErrors++;
    1006     }
    1007     rc = tstVDOpenCreateWriteMerge("VDI", "tmpVDBase.vdi", "tmpVDDiff.vdi", u32Seed);
    1008     if (RT_FAILURE(rc))
    1009     {
    1010         RTPrintf("tstVD: VDI test failed (existing image)! rc=%Rrc\n", rc);
    1011         g_cErrors++;
    1012     }
    1013 #endif /* VDI_TEST */
    1014 #ifdef VMDK_TEST
    1015     rc = tstVDOpenCreateWriteMerge("VMDK", "tmpVDBase.vmdk", "tmpVDDiff.vmdk", u32Seed);
    1016     if (RT_FAILURE(rc))
    1017     {
    1018         RTPrintf("tstVD: VMDK test failed (new image)! rc=%Rrc\n", rc);
    1019         g_cErrors++;
    1020     }
    1021     rc = tstVDOpenCreateWriteMerge("VMDK", "tmpVDBase.vmdk", "tmpVDDiff.vmdk", u32Seed);
    1022     if (RT_FAILURE(rc))
    1023     {
    1024         RTPrintf("tstVD: VMDK test failed (existing image)! rc=%Rrc\n", rc);
    1025         g_cErrors++;
    1026     }
    1027 #endif /* VMDK_TEST */
    1028 #ifdef VHD_TEST
    1029     rc = tstVDCreateWriteOpenRead("VHD", "tmpVDCreate.vhd", u32Seed);
    1030     if (RT_FAILURE(rc))
    1031     {
    1032         RTPrintf("tstVD: VHD test failed (creating image)! rc=%Rrc\n", rc);
    1033         g_cErrors++;
    1034     }
    1035 
    1036     rc = tstVDOpenCreateWriteMerge("VHD", "tmpVDBase.vhd", "tmpVDDiff.vhd", u32Seed);
    1037     if (RT_FAILURE(rc))
    1038     {
    1039         RTPrintf("tstVD: VHD test failed (existing image)! rc=%Rrc\n", rc);
    1040         g_cErrors++;
    1041     }
    1042 #endif /* VHD_TEST */
    1043157
    1044158    /*
     
    1046160     */
    1047161    RTFileDelete("tmpVDCreate.vdi");
    1048     RTFileDelete("tmpVDCreate.vmdk");
    1049     RTFileDelete("tmpVDCreate.vhd");
    1050     RTFileDelete("tmpVDBase.vdi");
    1051     RTFileDelete("tmpVDDiff.vdi");
    1052     RTFileDelete("tmpVDBase.vmdk");
    1053     RTFileDelete("tmpVDDiff.vmdk");
    1054     RTFileDelete("tmpVDBase.vhd");
    1055     RTFileDelete("tmpVDDiff.vhd");
    1056     RTFileDelete("tmpVDCreate-s001.vmdk");
    1057     RTFileDelete("tmpVDCreate-s002.vmdk");
    1058     RTFileDelete("tmpVDCreate-s003.vmdk");
    1059     RTFileDelete("tmpVDRename.vmdk");
    1060     RTFileDelete("tmpVDRename-s001.vmdk");
    1061     RTFileDelete("tmpVDRename-s002.vmdk");
    1062     RTFileDelete("tmpVDRename-s003.vmdk");
    1063162
    1064163    rc = VDShutdown();
     
    1069168    }
    1070169     /*
    1071      * Summary
    1072      */
     170      * Summary
     171      */
    1073172    if (!g_cErrors)
    1074173        RTPrintf("tstVD: SUCCESS\n");
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette