VirtualBox

vbox的更動 9262 路徑 trunk/src/VBox/Devices/Storage


忽略:
時間撮記:
2008-5-30 下午05:32:54 (16 年 以前)
作者:
vboxsync
訊息:

VDMerge testcase with pseudo random writes.

位置:
trunk/src/VBox/Devices/Storage
檔案:
修改 2 筆資料

圖例:

未更動
新增
刪除
  • trunk/src/VBox/Devices/Storage/VBoxHDD-new.cpp

    r8594 r9262  
    22162216 * @returns VERR_VDI_NOT_OPENED if no image is opened in HDD container.
    22172217 * @param   pDisk           Pointer to HDD container.
    2218  * @param   uOffset         Offset of first reading byte from start of disk.
     2218 * @param   uOffset         Offset of the first byte being
     2219 *                          written from start of disk.
    22192220 * @param   pvBuf           Pointer to buffer for writing data.
    22202221 * @param   cbWrite         Number of bytes to write.
  • trunk/src/VBox/Devices/Storage/testcase/tstVD.cpp

    r8248 r9262  
    2727#include <iprt/mem.h>
    2828#include <iprt/initterm.h>
     29#include <iprt/rand.h>
     30#include "stdio.h"
     31#include "stdlib.h"
    2932
    3033/*******************************************************************************
     
    8083}
    8184
    82 
     85#if 0
    8386static int tstVDOpenCreateWriteMerge(const char *pszBackend,
    8487                                     const char *pszBaseFilename,
     
    175178    return 0;
    176179}
    177 
    178 
    179 int main()
     180#else
     181
     182#undef RTDECL
     183#define RTDECL(x) static x
     184
     185/* Start of IPRT code */
     186
     187/**
     188 * The following code is based on the work of George Marsaglia
     189 * taken from
     190 * http://groups.google.ws/group/comp.sys.sun.admin/msg/7c667186f6cbf354
     191 * and
     192 * http://groups.google.ws/group/comp.lang.c/msg/0e170777c6e79e8d
     193 */
     194
     195/*
     196A C version of a very very good 64-bit RNG is given below.
     197You should be able to adapt it to your particular needs.
     198
     199It is based on the complimentary-multiple-with-carry
     200sequence
     201         x(n)=a*x(n-4)+carry mod 2^64-1,
     202which works as follows:
     203Assume a certain multiplier 'a' and a base 'b'.
     204Given a current x value and a current carry 'c',
     205form:               t=a*x+c
     206Then the new carry is     c=floor(t/b)
     207and the new x value is    x = b-1-(t mod b).
     208
     209
     210Ordinarily, for 32-bit mwc or cmwc sequences, the
     211value t=a*x+c can be formed in 64 bits, then the new c
     212is the top and the new x the bottom 32 bits (with a little
     213fiddling when b=2^32-1 and cmwc rather than mwc.)
     214
     215
     216To generate 64-bit x's, it is difficult to form
     217t=a*x+c in 128 bits then get the new c and new x
     218from the the top and bottom halves.
     219But if 'a' has a special form, for example,
     220a=2^62+2^47+2 and b=2^64-1, then the new c and
     221the new x can be formed with shifts, tests and +/-'s,
     222again with a little fiddling because b=2^64-1 rather
     223than 2^64.   (The latter is not an optimal choice because,
     224being a square, it cannot be a primitive root of the
     225prime a*b^k+1, where 'k' is the 'lag':
     226        x(n)=a*x(n-k)+carry mod b.)
     227But the multiplier a=2^62+2^47+2 makes a*b^4+1 a prime for
     228which b=2^64-1 is a primitive root, and getting  the new x and
     229new c  can be done with arithmetic on integers the size of x.
     230*/
     231
     232struct RndCtx
     233{
     234    uint64_t x;
     235    uint64_t y;
     236    uint64_t z;
     237    uint64_t w;
     238    uint64_t c;
     239    uint32_t u32x;
     240    uint32_t u32y;
     241};
     242typedef struct RndCtx RNDCTX;
     243typedef RNDCTX *PRNDCTX;
     244
     245/**
     246 * @todo I failed to make Windows' sscanf to process %llx format
     247 *       specification properly. If someone knows how to do it
     248 *       please re-write the format spec the way it stays the
     249 *       same on all platforms. We probably need RTStrScanf()
     250 *       anyway.
     251 */
     252
     253#define RT_PRND_SEED_FSPEC_OUT "%016llx#%016llx#%016llx#%016llx#%016llx#%08x#%08x"
     254#ifdef RT_OS_WINDOWS
     255# define RT_PRND_SEED_FSPEC_IN "%I64x#%I64x#%I64x#%I64x#%I64x#%x#%x"
     256#else
     257# define RT_PRND_SEED_FSPEC_IN "%llx#%llx#%llx#%llx#%llx#%x#%x"
     258#endif
     259
     260/**
     261 * Initialize seeds.
     262 * 
     263 * @remarks You should choose ANY 4 random 64-bit
     264 * seeds x,y,z,w < 2^64-1 and a random seed c in
     265 * 0<= c < a = 2^62+2^47+2.
     266 * There are P=(2^62+2^46+2)*(2^64-1)^4 > 2^318 possible choices
     267 * for seeds, the period of the RNG.
     268 */
     269RTDECL(int) RTPRandInit(PRNDCTX pCtx, const char *pszSeedInfo)
     270{
     271    if (pszSeedInfo)
     272    {
     273        int nFieldsRead = sscanf(pszSeedInfo, RT_PRND_SEED_FSPEC_IN,
     274               &pCtx->x, &pCtx->y, &pCtx->z, &pCtx->w, &pCtx->c, &pCtx->u32x, &pCtx->u32y);
     275        if (nFieldsRead != 7 || pCtx->x > UINT64_MAX-1 || pCtx->y > UINT64_MAX-1 ||
     276            pCtx->z > UINT64_MAX-1 || pCtx->w > UINT64_MAX-1 ||
     277            pCtx->c > ((1ull << 62) + (1ull << 47) + 1) || pCtx->u32y == 0)
     278            return VERR_INVALID_PARAMETER;
     279    }
     280    else
     281    {
     282        pCtx->x = RTRandU64Ex(0, UINT64_MAX-1);
     283        pCtx->y = RTRandU64Ex(0, UINT64_MAX-1);
     284        pCtx->z = RTRandU64Ex(0, UINT64_MAX-1);
     285        pCtx->w = RTRandU64Ex(0, UINT64_MAX-1);
     286        pCtx->c = RTRandU64Ex(0, (1ull << 62) + (1ull << 47) + 1);
     287        pCtx->u32x = RTRandU32();
     288        pCtx->u32y = RTRandU32Ex(1, UINT32_MAX);
     289    }
     290    return VINF_SUCCESS;
     291}
     292
     293RTDECL(int) RTPRandGetSeedInfo(PRNDCTX pCtx, char **ppszSeedInfo)
     294{
     295    return RTStrAPrintf(ppszSeedInfo, RT_PRND_SEED_FSPEC_OUT,
     296                          pCtx->x, pCtx->y, pCtx->z, pCtx->w, pCtx->c, pCtx->u32x, pCtx->u32y);
     297}
     298
     299/**
     300 * Generate a 64-bit unsigned random number.
     301 *
     302 * @returns The pseudo random number.
     303 */
     304RTDECL(uint64_t) RTPRandU64(PRNDCTX pCtx)
     305{
     306    uint64_t t;
     307    t = (pCtx->x<<47) + (pCtx->x<<62) + (pCtx->x<<1);
     308    t += pCtx->c; t+= (t < pCtx->c);
     309    pCtx->c = (t<pCtx->c) + (pCtx->x>>17) + (pCtx->x>>2) + (pCtx->x>>63);
     310    pCtx->x = pCtx->y;  pCtx->y = pCtx->z ; pCtx->z = pCtx->w;
     311    return (pCtx->w = ~(t + pCtx->c)-1);
     312}
     313
     314/**
     315 * Generate a 64-bit unsigned pseudo random number in the set
     316 * [u64First..u64Last].
     317 *
     318 * @returns The pseudo random number.
     319 * @param   u64First    First number in the set.
     320 * @param   u64Last     Last number in the set.
     321 */
     322RTDECL(uint64_t) RTPRandU64Ex(PRNDCTX pCtx, uint64_t u64First, uint64_t u64Last)
     323{
     324    if (u64First == 0 && u64Last == UINT64_MAX)
     325        return RTPRandU64(pCtx);
     326
     327    uint64_t u64Tmp;
     328    uint64_t u64Range = u64Last - u64First + 1;
     329    uint64_t u64Scale = UINT64_MAX / u64Range;
     330
     331    do
     332    {
     333        u64Tmp = RTPRandU64(pCtx) / u64Scale;
     334    } while (u64Tmp >= u64Range);
     335    return u64First + u64Tmp;
     336}
     337
     338/**
     339 * Generate a 32-bit unsigned random number.
     340 *
     341 * @returns The pseudo random number.
     342 */
     343RTDECL(uint32_t) RTPRandU32(PRNDCTX pCtx)
     344{
     345    return ( pCtx->u32x = 69069 * pCtx->u32x + 123,
     346             pCtx->u32y ^= pCtx->u32y<<13,
     347             pCtx->u32y ^= pCtx->u32y>>17,
     348             pCtx->u32y ^= pCtx->u32y<<5,
     349             pCtx->u32x + pCtx->u32y );
     350}
     351
     352/**
     353 * Generate a 32-bit unsigned pseudo random number in the set
     354 * [u32First..u32Last].
     355 *
     356 * @returns The pseudo random number.
     357 * @param   u32First    First number in the set.
     358 * @param   u32Last     Last number in the set.
     359 */
     360RTDECL(uint32_t) RTPRandU32Ex(PRNDCTX pCtx, uint32_t u32First, uint32_t u32Last)
     361{
     362    if (u32First == 0 && u32Last == UINT32_MAX)
     363        return RTPRandU32(pCtx);
     364
     365    uint32_t u32Tmp;
     366    uint32_t u32Range = u32Last - u32First + 1;
     367    uint32_t u32Scale = UINT32_MAX / u32Range;
     368
     369    do
     370    {
     371        u32Tmp = RTPRandU32(pCtx) / u32Scale;
     372    } while (u32Tmp >= u32Range);
     373    return u32First + u32Tmp;
     374}
     375
     376/* End of IPRT code */
     377
     378struct Segment
     379{
     380    uint64_t u64Offset;
     381    uint32_t u32Length;
     382    uint32_t u8Value;
     383};
     384typedef struct Segment *PSEGMENT;
     385
     386static void initializeRandomGenerator(PRNDCTX pCtx, const char *pszSeedInfo)
     387{
     388    int rc = RTPRandInit(pCtx, pszSeedInfo);
     389    if (VBOX_FAILURE(rc))
     390    {
     391        RTPrintf("ERROR: Failed to initialize random generator. RC=%Vrc\n", rc);
     392    }
     393    else
     394    {
     395        char *pszNewSeedInfo = NULL;
     396        rc = RTPRandGetSeedInfo(pCtx, &pszNewSeedInfo);
     397        if (VBOX_FAILURE(rc))
     398        {
     399            RTPrintf("ERROR: Failed to get seed values. RC=%Vrc\n", rc);
     400        }
     401        else
     402        {
     403            RTPrintf("INFO: Random generator seed used: %s\n", pszNewSeedInfo);
     404            RTMemFree(pszNewSeedInfo);
     405        }
     406    }
     407   
     408}
     409
     410static int compareSegments(const void *left, const void *right)
     411{
     412    /* Note that no duplicates are allowed in the array being sorted. */
     413    return ((PSEGMENT)left)->u64Offset < ((PSEGMENT)right)->u64Offset ? -1 : 1;
     414}
     415
     416static void generateRandomSegments(PRNDCTX pCtx, PSEGMENT pSegment, uint32_t nSegments, uint32_t u32MaxSegmentSize, uint64_t u64DiskSize, uint32_t u32SectorSize, uint8_t u8ValueLow, uint8_t u8ValueHigh)
     417{
     418    uint32_t i;
     419    /* Generate segment offsets. */
     420    for (i = 0; i < nSegments; i++)
     421    {
     422        bool fDuplicateFound = false;
     423        do
     424        {
     425            pSegment[i].u64Offset = RTPRandU64Ex(pCtx, 0, u64DiskSize / u32SectorSize - 1) * u32SectorSize;
     426            for (uint32_t j = 0; j < i; j++)
     427                if (pSegment[i].u64Offset == pSegment[j].u64Offset)
     428                    fDuplicateFound = true;
     429        } while (fDuplicateFound);
     430    }
     431    /* Sort in offset-ascending order. */
     432    qsort(pSegment, nSegments, sizeof(*pSegment), compareSegments);
     433    /* Put a sentinel at the end. */
     434    pSegment[nSegments].u64Offset = u64DiskSize;
     435    pSegment[nSegments].u32Length = 0;
     436    /* Generate segment lengths and values. */
     437    for (i = 0; i < nSegments; i++)
     438    {
     439        pSegment[i].u32Length = RTPRandU32Ex(pCtx, 1, RT_MIN(pSegment[i+1].u64Offset - pSegment[i].u64Offset,
     440                                                             u32MaxSegmentSize) / u32SectorSize) * u32SectorSize;
     441        pSegment[i].u8Value  = RTPRandU32Ex(pCtx, (uint32_t)u8ValueLow, (uint32_t)u8ValueHigh);
     442    }
     443}
     444
     445static void mergeSegments(PSEGMENT pBaseSegment, PSEGMENT pDiffSegment, PSEGMENT pMergeSegment)
     446{
     447    while (pBaseSegment->u32Length > 0 || pDiffSegment->u32Length > 0)
     448    {
     449        if (pBaseSegment->u64Offset < pDiffSegment->u64Offset)
     450        {
     451            *pMergeSegment = *pBaseSegment;
     452            if (pMergeSegment->u64Offset + pMergeSegment->u32Length <= pDiffSegment->u64Offset)
     453                pBaseSegment++;
     454            else
     455            {
     456                pMergeSegment->u32Length = pDiffSegment->u64Offset - pMergeSegment->u64Offset;
     457                if (pBaseSegment->u64Offset + pBaseSegment->u32Length >
     458                    pDiffSegment->u64Offset + pDiffSegment->u32Length)
     459                {
     460                    pBaseSegment->u32Length -= pBaseSegment->u64Offset -pDiffSegment->u64Offset - pDiffSegment->u32Length;
     461                    pBaseSegment->u64Offset = pDiffSegment->u64Offset + pDiffSegment->u32Length;
     462                }
     463                else
     464                    pBaseSegment++;
     465            }
     466            pMergeSegment++;
     467        }
     468        else
     469        {
     470            *pMergeSegment = *pDiffSegment;
     471            if (pMergeSegment->u64Offset + pMergeSegment->u32Length <= pBaseSegment->u64Offset)
     472            {
     473                pDiffSegment++;
     474                pMergeSegment++;
     475            }
     476            else
     477            {
     478                if (pBaseSegment->u64Offset + pBaseSegment->u32Length > pDiffSegment->u64Offset + pDiffSegment->u32Length)
     479                {
     480                    pBaseSegment->u32Length -= pDiffSegment->u64Offset + pDiffSegment->u32Length - pBaseSegment->u64Offset;
     481                    pBaseSegment->u64Offset = pDiffSegment->u64Offset + pDiffSegment->u32Length;
     482                    pDiffSegment++;
     483                    pMergeSegment++;
     484                }
     485                else
     486                    pBaseSegment++;
     487            }
     488        }
     489    }
     490}
     491
     492static void writeSegmentsToDisk(PVBOXHDD pVD, void *pvBuf, PSEGMENT pSegment)
     493{
     494    while (pSegment->u32Length)
     495    {
     496        //memset((uint8_t*)pvBuf + pSegment->u64Offset, pSegment->u8Value, pSegment->u32Length);
     497        memset(pvBuf, pSegment->u8Value, pSegment->u32Length);
     498        VDWrite(pVD, pSegment->u64Offset, pvBuf, pSegment->u32Length);
     499        pSegment++;
     500    }
     501}
     502
     503static int readAndCompareSegments(PVBOXHDD pVD, void *pvBuf, PSEGMENT pSegment)
     504{
     505    while (pSegment->u32Length)
     506    {
     507        int rc = VDRead(pVD, pSegment->u64Offset, pvBuf, pSegment->u32Length);
     508        if (VBOX_FAILURE(rc))
     509        {
     510            RTPrintf("ERROR: Failed to read from virtual disk\n");
     511            return rc;
     512        }
     513        else
     514        {
     515            for (unsigned i = 0; i < pSegment->u32Length; i++)
     516                if (((uint8_t*)pvBuf)[i] != pSegment->u8Value)
     517                {
     518                    RTPrintf("ERROR: Segment at %Lx of %d bytes is corrupt at offset %x (found %x instead of %x)\n",
     519                             pSegment->u64Offset, pSegment->u32Length, i, ((uint8_t*)pvBuf)[i],
     520                             pSegment->u8Value);
     521                    return VERR_INTERNAL_ERROR;
     522                }
     523        }
     524        pSegment++;
     525    }
     526
     527    return VINF_SUCCESS;
     528}
     529
     530static int tstVDOpenCreateWriteMerge(const char *pszBackend,
     531                                     const char *pszBaseFilename,
     532                                     const char *pszDiffFilename,
     533                                     const char *pszSeedInfo)
    180534{
    181535    int rc;
     536    PVBOXHDD pVD = NULL;
     537    char *pszFormat;
     538    PDMMEDIAGEOMETRY PCHS = { 0, 0, 0 };
     539    PDMMEDIAGEOMETRY LCHS = { 0, 0, 0 };
     540    uint64_t u64DiskSize  = 1000 * _1M;
     541    uint32_t u32SectorSize = 512;
     542
     543#define CHECK(str) \
     544    do \
     545    { \
     546        RTPrintf("%s rc=%Vrc\n", str, rc); \
     547        if (VBOX_FAILURE(rc)) \
     548        { \
     549            VDCloseAll(pVD); \
     550            return rc; \
     551        } \
     552    } while (0)
     553
     554    rc = VDCreate(tstVDError, NULL, &pVD);
     555    CHECK("VDCreate()");
     556
     557    RTFILE File;
     558    rc = RTFileOpen(&File, pszBaseFilename, RTFILE_O_READ);
     559    if (VBOX_SUCCESS(rc))
     560    {
     561        RTFileClose(File);
     562        rc = VDGetFormat(pszBaseFilename, &pszFormat);
     563        RTPrintf("VDGetFormat() pszFormat=%s rc=%Vrc\n", pszFormat, rc);
     564        if (VBOX_SUCCESS(rc) && strcmp(pszFormat, pszBackend))
     565        {
     566            rc = VERR_GENERAL_FAILURE;
     567            RTPrintf("VDGetFormat() returned incorrect backend name\n");
     568        }
     569        RTStrFree(pszFormat);
     570        CHECK("VDGetFormat()");
     571
     572        rc = VDOpen(pVD, pszBackend, pszBaseFilename, VD_OPEN_FLAGS_NORMAL);
     573        CHECK("VDOpen()");
     574    }
     575    else
     576    {
     577        rc = VDCreateBase(pVD, pszBackend, pszBaseFilename,
     578                          VD_IMAGE_TYPE_NORMAL, u64DiskSize,
     579                          VD_IMAGE_FLAGS_NONE, "Test image",
     580                          &PCHS, &LCHS, VD_OPEN_FLAGS_NORMAL,
     581                          NULL, NULL);
     582        CHECK("VDCreateBase()");
     583    }
     584
     585    int nSegments = 10;
     586    /* Allocate one extra element for a sentinel. */
     587    PSEGMENT paBaseSegments  = (PSEGMENT)RTMemAllocZ(sizeof(struct Segment) * (nSegments + 1));
     588    PSEGMENT paDiffSegments  = (PSEGMENT)RTMemAllocZ(sizeof(struct Segment) * (nSegments + 1));
     589    PSEGMENT paMergeSegments = (PSEGMENT)RTMemAllocZ(sizeof(struct Segment) * (nSegments + 1) * 3);
     590
     591    void *pvBuf = RTMemAlloc(_1M);
     592
     593    RNDCTX ctx;
     594    initializeRandomGenerator(&ctx, pszSeedInfo);
     595    generateRandomSegments(&ctx, paBaseSegments, nSegments, _1M, u64DiskSize, u32SectorSize, 0u, 127u);
     596    generateRandomSegments(&ctx, paDiffSegments, nSegments, _1M, u64DiskSize, u32SectorSize, 128u, 255u);
     597
     598    PSEGMENT pSegment;
     599    /*RTPrintf("Base segments:\n");
     600    for (pSegment = paBaseSegments; pSegment->u32Length; pSegment++)
     601        RTPrintf("off: %08Lx len: %04x val: %02x\n", pSegment->u64Offset, pSegment->u32Length, pSegment->u8Value);*/
     602    writeSegmentsToDisk(pVD, pvBuf, paBaseSegments);
     603
     604    rc = VDCreateDiff(pVD, pszBackend, pszDiffFilename,
     605                      VD_IMAGE_FLAGS_NONE, "Test diff image",
     606                      VD_OPEN_FLAGS_NORMAL, NULL, NULL);
     607    CHECK("VDCreateDiff()");
     608
     609    /*RTPrintf("\nDiff segments:\n");
     610    for (pSegment = paDiffSegments; pSegment->u32Length; pSegment++)
     611        RTPrintf("off: %08Lx len: %04x val: %02x\n", pSegment->u64Offset, pSegment->u32Length, pSegment->u8Value);*/
     612    writeSegmentsToDisk(pVD, pvBuf, paDiffSegments);
     613
     614    VDDumpImages(pVD);
     615
     616    RTPrintf("Merging diff into base..\n");
     617    rc = VDMerge(pVD, -1, 0, NULL, NULL);
     618    CHECK("VDMerge()");
     619
     620    mergeSegments(paBaseSegments, paDiffSegments, paMergeSegments);
     621    /*RTPrintf("\nMerged segments:\n");
     622    for (pSegment = paMergeSegments; pSegment->u32Length; pSegment++)
     623        RTPrintf("off: %08Lx len: %04x val: %02x\n", pSegment->u64Offset, pSegment->u32Length, pSegment->u8Value);*/
     624    rc = readAndCompareSegments(pVD, pvBuf, paMergeSegments);
     625    CHECK("readAndCompareSegments()");
     626
     627    RTMemFree(paMergeSegments);
     628    RTMemFree(paDiffSegments);
     629    RTMemFree(paBaseSegments);
     630
     631    VDDumpImages(pVD);
     632
     633    VDCloseAll(pVD);
     634#undef CHECK
     635    return 0;
     636}
     637#endif
     638
     639int main(int argc, char *argv[])
     640{
     641    int rc;
     642
     643    const char *pszSeedInfo = NULL;
     644
     645    if (argc > 1)
     646        pszSeedInfo = argv[1];
    182647
    183648    RTR3Init();
     
    243708    }
    244709
    245     rc = tstVDOpenCreateWriteMerge("VDI", "tmpVDBase.vdi", "tmpVDDiff.vdi");
     710    rc = tstVDOpenCreateWriteMerge("VDI", "tmpVDBase.vdi", "tmpVDDiff.vdi", pszSeedInfo);
    246711    if (VBOX_FAILURE(rc))
    247712    {
     
    249714        g_cErrors++;
    250715    }
    251     rc = tstVDOpenCreateWriteMerge("VDI", "tmpVDBase.vdi", "tmpVDDiff.vdi");
     716    rc = tstVDOpenCreateWriteMerge("VDI", "tmpVDBase.vdi", "tmpVDDiff.vdi", pszSeedInfo);
    252717    if (VBOX_FAILURE(rc))
    253718    {
     
    255720        g_cErrors++;
    256721    }
    257     rc = tstVDOpenCreateWriteMerge("VMDK", "tmpVDBase.vmdk", "tmpVDDiff.vmdk");
     722    rc = tstVDOpenCreateWriteMerge("VMDK", "tmpVDBase.vmdk", "tmpVDDiff.vmdk", pszSeedInfo);
    258723    if (VBOX_FAILURE(rc))
    259724    {
     
    261726        g_cErrors++;
    262727    }
    263     rc = tstVDOpenCreateWriteMerge("VMDK", "tmpVDBase.vmdk", "tmpVDDiff.vmdk");
     728    rc = tstVDOpenCreateWriteMerge("VMDK", "tmpVDBase.vmdk", "tmpVDDiff.vmdk", pszSeedInfo);
    264729    if (VBOX_FAILURE(rc))
    265730    {
     
    277742    RTFileDelete("tmpVDBase.vmdk");
    278743    RTFileDelete("tmpVDDiff.vmdk");
     744#if 0
     745    rc = tstVDOpenCreateWriteMerge("VDI", "tmpVDBase.vdi", "tmpVDDiff.vdi", pszSeedInfo);
     746    if (VBOX_FAILURE(rc))
     747    {
     748        RTPrintf("tstVD: VHD test failed (existing image)! rc=%Vrc\n", rc);
     749        g_cErrors++;
     750    }
     751    RTFileDelete("tmpVDBase.vdi");
     752    RTFileDelete("tmpVDDiff.vdi");
     753    //RTFileDelete("tmpVDDiff.vhd");
     754#endif
    279755
    280756    /*
注意: 瀏覽 TracChangeset 來幫助您使用更動檢視器

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