VirtualBox

source: vbox/trunk/src/VBox/Storage/testcase/tstVDIo.cpp@ 52378

Last change on this file since 52378 was 52378, checked in by vboxsync, 11 years ago

Storage/tstVDIo: Don't run builtin tests if there is not enough free RAM available

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 92.5 KB
Line 
1/* $Id: tstVDIo.cpp 52378 2014-08-14 11:44:04Z vboxsync $ */
2/** @file
3 *
4 * VBox HDD container test utility - I/O replay.
5 */
6
7/*
8 * Copyright (C) 2011-2014 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.215389.xyz. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18#define LOGGROUP LOGGROUP_DEFAULT
19#include <VBox/vd.h>
20#include <VBox/vddbg.h>
21#include <VBox/err.h>
22#include <VBox/log.h>
23#include <iprt/asm.h>
24#include <iprt/string.h>
25#include <iprt/stream.h>
26#include <iprt/mem.h>
27#include <iprt/initterm.h>
28#include <iprt/getopt.h>
29#include <iprt/list.h>
30#include <iprt/ctype.h>
31#include <iprt/semaphore.h>
32#include <iprt/thread.h>
33#include <iprt/rand.h>
34#include <iprt/critsect.h>
35#include <iprt/test.h>
36#include <iprt/system.h>
37
38#include "VDMemDisk.h"
39#include "VDIoBackend.h"
40#include "VDIoRnd.h"
41
42#include "VDScript.h"
43#include "BuiltinTests.h"
44
45/** forward declaration for the global test data pointer. */
46typedef struct VDTESTGLOB *PVDTESTGLOB;
47
48/**
49 * A virtual file backed by memory.
50 */
51typedef struct VDFILE
52{
53 /** Pointer to the next file. */
54 RTLISTNODE Node;
55 /** Name of the file. */
56 char *pszName;
57 /** Storage backing the file. */
58 PVDIOSTORAGE pIoStorage;
59 /** Flag whether the file is read locked. */
60 bool fReadLock;
61 /** Flag whether the file is write locked. */
62 bool fWriteLock;
63 /** Statistics: Number of reads. */
64 unsigned cReads;
65 /** Statistics: Number of writes. */
66 unsigned cWrites;
67 /** Statistics: Number of flushes. */
68 unsigned cFlushes;
69 /** Statistics: Number of async reads. */
70 unsigned cAsyncReads;
71 /** Statistics: Number of async writes. */
72 unsigned cAsyncWrites;
73 /** Statistics: Number of async flushes. */
74 unsigned cAsyncFlushes;
75} VDFILE, *PVDFILE;
76
77/**
78 * VD storage object.
79 */
80typedef struct VDSTORAGE
81{
82 /** Pointer to the file. */
83 PVDFILE pFile;
84 /** Completion callback of the VD layer. */
85 PFNVDCOMPLETED pfnComplete;
86} VDSTORAGE, *PVDSTORAGE;
87
88/**
89 * A virtual disk.
90 */
91typedef struct VDDISK
92{
93 /** List node. */
94 RTLISTNODE ListNode;
95 /** Name of the disk handle for identification. */
96 char *pszName;
97 /** HDD handle to operate on. */
98 PVBOXHDD pVD;
99 /** Memory disk used for data verification. */
100 PVDMEMDISK pMemDiskVerify;
101 /** Critical section to serialize access to the memory disk. */
102 RTCRITSECT CritSectVerify;
103 /** Physical CHS Geometry. */
104 VDGEOMETRY PhysGeom;
105 /** Logical CHS geometry. */
106 VDGEOMETRY LogicalGeom;
107 /** Global test data. */
108 PVDTESTGLOB pTestGlob;
109} VDDISK, *PVDDISK;
110
111/**
112 * A data buffer with a pattern.
113 */
114typedef struct VDPATTERN
115{
116 /** List node. */
117 RTLISTNODE ListNode;
118 /** Name of the pattern. */
119 char *pszName;
120 /** Size of the pattern. */
121 size_t cbPattern;
122 /** Pointer to the buffer containing the pattern. */
123 void *pvPattern;
124} VDPATTERN, *PVDPATTERN;
125
126/**
127 * Global VD test state.
128 */
129typedef struct VDTESTGLOB
130{
131 /** List of active virtual disks. */
132 RTLISTNODE ListDisks;
133 /** Head of the active file list. */
134 RTLISTNODE ListFiles;
135 /** Head of the pattern list. */
136 RTLISTNODE ListPatterns;
137 /** I/O backend, common data. */
138 PVDIOBACKEND pIoBackend;
139 /** Error interface. */
140 VDINTERFACEERROR VDIfError;
141 /** Pointer to the per disk interface list. */
142 PVDINTERFACE pInterfacesDisk;
143 /** I/O interface. */
144 VDINTERFACEIO VDIfIo;
145 /** Pointer to the per image interface list. */
146 PVDINTERFACE pInterfacesImages;
147 /** I/O RNG handle. */
148 PVDIORND pIoRnd;
149 /** Current storage backend to use. */
150 char *pszIoBackend;
151 /** Testcase handle. */
152 RTTEST hTest;
153} VDTESTGLOB;
154
155/**
156 * Transfer direction.
157 */
158typedef enum VDIOREQTXDIR
159{
160 VDIOREQTXDIR_READ = 0,
161 VDIOREQTXDIR_WRITE,
162 VDIOREQTXDIR_FLUSH,
163 VDIOREQTXDIR_DISCARD
164} VDIOREQTXDIR;
165
166/**
167 * I/O request.
168 */
169typedef struct VDIOREQ
170{
171 /** Transfer type. */
172 VDIOREQTXDIR enmTxDir;
173 /** slot index. */
174 unsigned idx;
175 /** Start offset. */
176 uint64_t off;
177 /** Size to transfer. */
178 size_t cbReq;
179 /** S/G Buffer */
180 RTSGBUF SgBuf;
181 /** Data segment */
182 RTSGSEG DataSeg;
183 /** Flag whether the request is outstanding or not. */
184 volatile bool fOutstanding;
185 /** Buffer to use for reads. */
186 void *pvBufRead;
187 /** Opaque user data. */
188 void *pvUser;
189} VDIOREQ, *PVDIOREQ;
190
191/**
192 * I/O test data.
193 */
194typedef struct VDIOTEST
195{
196 /** Start offset. */
197 uint64_t offStart;
198 /** End offset. */
199 uint64_t offEnd;
200 /** Flag whether random or sequential access is wanted */
201 bool fRandomAccess;
202 /** Block size. */
203 size_t cbBlkIo;
204 /** Number of bytes to transfer. */
205 uint64_t cbIo;
206 /** Chance in percent to get a write. */
207 unsigned uWriteChance;
208 /** Pointer to the I/O data generator. */
209 PVDIORND pIoRnd;
210 /** Pointer to the data pattern to use. */
211 PVDPATTERN pPattern;
212 /** Data dependent on the I/O mode (sequential or random). */
213 union
214 {
215 /** Next offset for sequential access. */
216 uint64_t offNext;
217 /** Data for random acess. */
218 struct
219 {
220 /** Number of valid entries in the bitmap. */
221 uint32_t cBlocks;
222 /** Pointer to the bitmap marking accessed blocks. */
223 uint8_t *pbMapAccessed;
224 /** Number of unaccessed blocks. */
225 uint32_t cBlocksLeft;
226 } Rnd;
227 } u;
228} VDIOTEST, *PVDIOTEST;
229
230static DECLCALLBACK(int) vdScriptHandlerCreate(PVDSCRIPTARG paScriptArgs, void *pvUser);
231static DECLCALLBACK(int) vdScriptHandlerOpen(PVDSCRIPTARG paScriptArgs, void *pvUser);
232static DECLCALLBACK(int) vdScriptHandlerIo(PVDSCRIPTARG paScriptArgs, void *pvUser);
233static DECLCALLBACK(int) vdScriptHandlerFlush(PVDSCRIPTARG paScriptArgs, void *pvUser);
234static DECLCALLBACK(int) vdScriptHandlerMerge(PVDSCRIPTARG paScriptArgs, void *pvUser);
235static DECLCALLBACK(int) vdScriptHandlerCompact(PVDSCRIPTARG paScriptArgs, void *pvUser);
236static DECLCALLBACK(int) vdScriptHandlerDiscard(PVDSCRIPTARG paScriptArgs, void *pvUser);
237static DECLCALLBACK(int) vdScriptHandlerCopy(PVDSCRIPTARG paScriptArgs, void *pvUser);
238static DECLCALLBACK(int) vdScriptHandlerClose(PVDSCRIPTARG paScriptArgs, void *pvUser);
239static DECLCALLBACK(int) vdScriptHandlerPrintFileSize(PVDSCRIPTARG paScriptArgs, void *pvUser);
240static DECLCALLBACK(int) vdScriptHandlerIoLogReplay(PVDSCRIPTARG paScriptArgs, void *pvUser);
241static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDSCRIPTARG paScriptArgs, void *pvUser);
242static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser);
243static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDSCRIPTARG paScriptArgs, void *pvUser);
244static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDSCRIPTARG paScriptArgs, void *pvUser);
245static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser);
246static DECLCALLBACK(int) vdScriptHandlerSleep(PVDSCRIPTARG paScriptArgs, void *pvUser);
247static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDSCRIPTARG paScriptArgs, void *pvUser);
248static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDSCRIPTARG paScriptArgs, void *pvUser);
249static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDSCRIPTARG paScriptArgs, void *pvUser);
250static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDSCRIPTARG paScriptArgs, void *pvUser);
251static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDSCRIPTARG paScriptArgs, void *pvUser);
252static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDSCRIPTARG paScriptArgs, void *pvUser);
253static DECLCALLBACK(int) vdScriptHandlerShowStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser);
254static DECLCALLBACK(int) vdScriptHandlerResetStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser);
255static DECLCALLBACK(int) vdScriptHandlerResize(PVDSCRIPTARG paScriptArgs, void *pvUser);
256static DECLCALLBACK(int) vdScriptHandlerSetFileBackend(PVDSCRIPTARG paScriptArgs, void *pvUser);
257
258/* create action */
259const VDSCRIPTTYPE g_aArgCreate[] =
260{
261 VDSCRIPTTYPE_STRING,
262 VDSCRIPTTYPE_STRING,
263 VDSCRIPTTYPE_STRING,
264 VDSCRIPTTYPE_STRING,
265 VDSCRIPTTYPE_STRING,
266 VDSCRIPTTYPE_UINT64,
267 VDSCRIPTTYPE_BOOL,
268 VDSCRIPTTYPE_BOOL
269};
270
271/* open action */
272const VDSCRIPTTYPE g_aArgOpen[] =
273{
274 VDSCRIPTTYPE_STRING, /* disk */
275 VDSCRIPTTYPE_STRING, /* name */
276 VDSCRIPTTYPE_STRING, /* backend */
277 VDSCRIPTTYPE_BOOL, /* async */
278 VDSCRIPTTYPE_BOOL, /* shareable */
279 VDSCRIPTTYPE_BOOL, /* readonly */
280 VDSCRIPTTYPE_BOOL, /* discard */
281 VDSCRIPTTYPE_BOOL, /* ignoreflush */
282 VDSCRIPTTYPE_BOOL, /* honorsame */
283};
284
285/* I/O action */
286const VDSCRIPTTYPE g_aArgIo[] =
287{
288 VDSCRIPTTYPE_STRING, /* disk */
289 VDSCRIPTTYPE_BOOL, /* async */
290 VDSCRIPTTYPE_UINT32, /* max-reqs */
291 VDSCRIPTTYPE_STRING, /* mode */
292 VDSCRIPTTYPE_UINT64, /* size */
293 VDSCRIPTTYPE_UINT64, /* blocksize */
294 VDSCRIPTTYPE_UINT64, /* offStart */
295 VDSCRIPTTYPE_UINT64, /* offEnd */
296 VDSCRIPTTYPE_UINT32, /* writes */
297 VDSCRIPTTYPE_STRING /* pattern */
298};
299
300/* flush action */
301const VDSCRIPTTYPE g_aArgFlush[] =
302{
303 VDSCRIPTTYPE_STRING, /* disk */
304 VDSCRIPTTYPE_BOOL /* async */
305};
306
307/* merge action */
308const VDSCRIPTTYPE g_aArgMerge[] =
309{
310 VDSCRIPTTYPE_STRING, /* disk */
311 VDSCRIPTTYPE_UINT32, /* from */
312 VDSCRIPTTYPE_UINT32 /* to */
313};
314
315/* Compact a disk */
316const VDSCRIPTTYPE g_aArgCompact[] =
317{
318 VDSCRIPTTYPE_STRING, /* disk */
319 VDSCRIPTTYPE_UINT32 /* image */
320};
321
322/* Discard a part of a disk */
323const VDSCRIPTTYPE g_aArgDiscard[] =
324{
325 VDSCRIPTTYPE_STRING, /* disk */
326 VDSCRIPTTYPE_BOOL, /* async */
327 VDSCRIPTTYPE_STRING /* ranges */
328};
329
330/* Compact a disk */
331const VDSCRIPTTYPE g_aArgCopy[] =
332{
333 VDSCRIPTTYPE_STRING, /* diskfrom */
334 VDSCRIPTTYPE_STRING, /* diskto */
335 VDSCRIPTTYPE_UINT32, /* imagefrom */
336 VDSCRIPTTYPE_STRING, /* backend */
337 VDSCRIPTTYPE_STRING, /* filename */
338 VDSCRIPTTYPE_BOOL, /* movebyrename */
339 VDSCRIPTTYPE_UINT64, /* size */
340 VDSCRIPTTYPE_UINT32, /* fromsame */
341 VDSCRIPTTYPE_UINT32 /* tosame */
342};
343
344/* close action */
345const VDSCRIPTTYPE g_aArgClose[] =
346{
347 VDSCRIPTTYPE_STRING, /* disk */
348 VDSCRIPTTYPE_STRING, /* mode */
349 VDSCRIPTTYPE_BOOL /* delete */
350};
351
352/* print file size action */
353const VDSCRIPTTYPE g_aArgPrintFileSize[] =
354{
355 VDSCRIPTTYPE_STRING, /* disk */
356 VDSCRIPTTYPE_UINT32 /* image */
357};
358
359/* print file size action */
360const VDSCRIPTTYPE g_aArgIoLogReplay[] =
361{
362 VDSCRIPTTYPE_STRING, /* disk */
363 VDSCRIPTTYPE_STRING /* iolog */
364};
365
366/* I/O RNG create action */
367const VDSCRIPTTYPE g_aArgIoRngCreate[] =
368{
369 VDSCRIPTTYPE_UINT32, /* size */
370 VDSCRIPTTYPE_STRING, /* mode */
371 VDSCRIPTTYPE_UINT32, /* seed */
372};
373
374/* I/O pattern create action */
375const VDSCRIPTTYPE g_aArgIoPatternCreateFromNumber[] =
376{
377 VDSCRIPTTYPE_STRING, /* name */
378 VDSCRIPTTYPE_UINT32, /* size */
379 VDSCRIPTTYPE_UINT32 /* pattern */
380};
381
382/* I/O pattern create action */
383const VDSCRIPTTYPE g_aArgIoPatternCreateFromFile[] =
384{
385 VDSCRIPTTYPE_STRING, /* name */
386 VDSCRIPTTYPE_STRING /* file */
387};
388
389/* I/O pattern destroy action */
390const VDSCRIPTTYPE g_aArgIoPatternDestroy[] =
391{
392 VDSCRIPTTYPE_STRING /* name */
393};
394
395/* Sleep */
396const VDSCRIPTTYPE g_aArgSleep[] =
397{
398 VDSCRIPTTYPE_UINT32 /* time */
399};
400
401/* Dump memory file */
402const VDSCRIPTTYPE g_aArgDumpFile[] =
403{
404 VDSCRIPTTYPE_STRING, /* file */
405 VDSCRIPTTYPE_STRING /* path */
406};
407
408/* Create virtual disk handle */
409const VDSCRIPTTYPE g_aArgCreateDisk[] =
410{
411 VDSCRIPTTYPE_STRING, /* name */
412 VDSCRIPTTYPE_BOOL /* verify */
413};
414
415/* Create virtual disk handle */
416const VDSCRIPTTYPE g_aArgDestroyDisk[] =
417{
418 VDSCRIPTTYPE_STRING /* name */
419};
420
421/* Compare virtual disks */
422const VDSCRIPTTYPE g_aArgCompareDisks[] =
423{
424 VDSCRIPTTYPE_STRING, /* disk1 */
425 VDSCRIPTTYPE_STRING /* disk2 */
426};
427
428/* Dump disk info */
429const VDSCRIPTTYPE g_aArgDumpDiskInfo[] =
430{
431 VDSCRIPTTYPE_STRING /* disk */
432};
433
434/* Print message */
435const VDSCRIPTTYPE g_aArgPrintMsg[] =
436{
437 VDSCRIPTTYPE_STRING /* msg */
438};
439
440/* Show statistics */
441const VDSCRIPTTYPE g_aArgShowStatistics[] =
442{
443 VDSCRIPTTYPE_STRING /* file */
444};
445
446/* Reset statistics */
447const VDSCRIPTTYPE g_aArgResetStatistics[] =
448{
449 VDSCRIPTTYPE_STRING /* file */
450};
451
452/* Resize disk. */
453const VDSCRIPTTYPE g_aArgResize[] =
454{
455 VDSCRIPTTYPE_STRING, /* disk */
456 VDSCRIPTTYPE_UINT64 /* size */
457};
458
459/* Set file backend. */
460const VDSCRIPTTYPE g_aArgSetFileBackend[] =
461{
462 VDSCRIPTTYPE_STRING /* new file backend */
463};
464
465const VDSCRIPTCALLBACK g_aScriptActions[] =
466{
467 /* pcszFnName enmTypeReturn paArgDesc cArgDescs pfnHandler */
468 {"create", VDSCRIPTTYPE_VOID, g_aArgCreate, RT_ELEMENTS(g_aArgCreate), vdScriptHandlerCreate},
469 {"open", VDSCRIPTTYPE_VOID, g_aArgOpen, RT_ELEMENTS(g_aArgOpen), vdScriptHandlerOpen},
470 {"io", VDSCRIPTTYPE_VOID, g_aArgIo, RT_ELEMENTS(g_aArgIo), vdScriptHandlerIo},
471 {"flush", VDSCRIPTTYPE_VOID, g_aArgFlush, RT_ELEMENTS(g_aArgFlush), vdScriptHandlerFlush},
472 {"close", VDSCRIPTTYPE_VOID, g_aArgClose, RT_ELEMENTS(g_aArgClose), vdScriptHandlerClose},
473 {"printfilesize", VDSCRIPTTYPE_VOID, g_aArgPrintFileSize, RT_ELEMENTS(g_aArgPrintFileSize), vdScriptHandlerPrintFileSize},
474 {"ioreplay", VDSCRIPTTYPE_VOID, g_aArgIoLogReplay, RT_ELEMENTS(g_aArgIoLogReplay), vdScriptHandlerIoLogReplay},
475 {"merge", VDSCRIPTTYPE_VOID, g_aArgMerge, RT_ELEMENTS(g_aArgMerge), vdScriptHandlerMerge},
476 {"compact", VDSCRIPTTYPE_VOID, g_aArgCompact, RT_ELEMENTS(g_aArgCompact), vdScriptHandlerCompact},
477 {"discard", VDSCRIPTTYPE_VOID, g_aArgDiscard, RT_ELEMENTS(g_aArgDiscard), vdScriptHandlerDiscard},
478 {"copy", VDSCRIPTTYPE_VOID, g_aArgCopy, RT_ELEMENTS(g_aArgCopy), vdScriptHandlerCopy},
479 {"iorngcreate", VDSCRIPTTYPE_VOID, g_aArgIoRngCreate, RT_ELEMENTS(g_aArgIoRngCreate), vdScriptHandlerIoRngCreate},
480 {"iorngdestroy", VDSCRIPTTYPE_VOID, NULL, 0, vdScriptHandlerIoRngDestroy},
481 {"iopatterncreatefromnumber", VDSCRIPTTYPE_VOID, g_aArgIoPatternCreateFromNumber, RT_ELEMENTS(g_aArgIoPatternCreateFromNumber), vdScriptHandlerIoPatternCreateFromNumber},
482 {"iopatterncreatefromfile", VDSCRIPTTYPE_VOID, g_aArgIoPatternCreateFromFile, RT_ELEMENTS(g_aArgIoPatternCreateFromFile), vdScriptHandlerIoPatternCreateFromFile},
483 {"iopatterndestroy", VDSCRIPTTYPE_VOID, g_aArgIoPatternDestroy, RT_ELEMENTS(g_aArgIoPatternDestroy), vdScriptHandlerIoPatternDestroy},
484 {"sleep", VDSCRIPTTYPE_VOID, g_aArgSleep, RT_ELEMENTS(g_aArgSleep), vdScriptHandlerSleep},
485 {"dumpfile", VDSCRIPTTYPE_VOID, g_aArgDumpFile, RT_ELEMENTS(g_aArgDumpFile), vdScriptHandlerDumpFile},
486 {"createdisk", VDSCRIPTTYPE_VOID, g_aArgCreateDisk, RT_ELEMENTS(g_aArgCreateDisk), vdScriptHandlerCreateDisk},
487 {"destroydisk", VDSCRIPTTYPE_VOID, g_aArgDestroyDisk, RT_ELEMENTS(g_aArgDestroyDisk), vdScriptHandlerDestroyDisk},
488 {"comparedisks", VDSCRIPTTYPE_VOID, g_aArgCompareDisks, RT_ELEMENTS(g_aArgCompareDisks), vdScriptHandlerCompareDisks},
489 {"dumpdiskinfo", VDSCRIPTTYPE_VOID, g_aArgDumpDiskInfo, RT_ELEMENTS(g_aArgDumpDiskInfo), vdScriptHandlerDumpDiskInfo},
490 {"print", VDSCRIPTTYPE_VOID, g_aArgPrintMsg, RT_ELEMENTS(g_aArgPrintMsg), vdScriptHandlerPrintMsg},
491 {"showstatistics", VDSCRIPTTYPE_VOID, g_aArgShowStatistics, RT_ELEMENTS(g_aArgShowStatistics), vdScriptHandlerShowStatistics},
492 {"resetstatistics", VDSCRIPTTYPE_VOID, g_aArgResetStatistics, RT_ELEMENTS(g_aArgResetStatistics), vdScriptHandlerResetStatistics},
493 {"resize", VDSCRIPTTYPE_VOID, g_aArgResize, RT_ELEMENTS(g_aArgResize), vdScriptHandlerResize},
494 {"setfilebackend", VDSCRIPTTYPE_VOID, g_aArgSetFileBackend, RT_ELEMENTS(g_aArgSetFileBackend), vdScriptHandlerSetFileBackend},
495};
496
497const unsigned g_cScriptActions = RT_ELEMENTS(g_aScriptActions);
498
499static DECLCALLBACK(int) vdScriptCallbackPrint(PVDSCRIPTARG paScriptArgs, void *pvUser)
500{
501 RTPrintf(paScriptArgs[0].psz);
502 return VINF_SUCCESS;
503}
504
505static void tstVDError(void *pvUser, int rc, RT_SRC_POS_DECL,
506 const char *pszFormat, va_list va)
507{
508 RTPrintf("tstVD: Error %Rrc at %s:%u (%s): ", rc, RT_SRC_POS_ARGS);
509 RTPrintfV(pszFormat, va);
510 RTPrintf("\n");
511}
512
513static int tstVDMessage(void *pvUser, const char *pszFormat, va_list va)
514{
515 RTPrintf("tstVD: ");
516 RTPrintfV(pszFormat, va);
517 return VINF_SUCCESS;
518}
519
520static int tstVDIoTestInit(PVDIOTEST pIoTest, PVDTESTGLOB pGlob, bool fRandomAcc, uint64_t cbIo,
521 size_t cbBlkSize, uint64_t offStart, uint64_t offEnd,
522 unsigned uWriteChance, PVDPATTERN pPattern);
523static bool tstVDIoTestRunning(PVDIOTEST pIoTest);
524static void tstVDIoTestDestroy(PVDIOTEST pIoTest);
525static bool tstVDIoTestReqOutstanding(PVDIOREQ pIoReq);
526static int tstVDIoTestReqInit(PVDIOTEST pIoTest, PVDIOREQ pIoReq, void *pvUser);
527static void tstVDIoTestReqComplete(void *pvUser1, void *pvUser2, int rcReq);
528
529static PVDDISK tstVDIoGetDiskByName(PVDTESTGLOB pGlob, const char *pcszDisk);
530static PVDPATTERN tstVDIoGetPatternByName(PVDTESTGLOB pGlob, const char *pcszName);
531static PVDPATTERN tstVDIoPatternCreate(const char *pcszName, size_t cbPattern);
532static int tstVDIoPatternGetBuffer(PVDPATTERN pPattern, void **ppv, size_t cb);
533
534static DECLCALLBACK(int) vdScriptHandlerCreate(PVDSCRIPTARG paScriptArgs, void *pvUser)
535{
536 int rc = VINF_SUCCESS;
537 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
538 uint64_t cbSize = 0;
539 const char *pcszBackend = NULL;
540 const char *pcszImage = NULL;
541 const char *pcszDisk = NULL;
542 PVDDISK pDisk = NULL;
543 bool fBase = false;
544 bool fDynamic = true;
545 bool fIgnoreFlush = false;
546 bool fHonorSame = false;
547 PVDIOBACKEND pIoBackend = NULL;
548
549 pcszDisk = paScriptArgs[0].psz;
550 if (!RTStrICmp(paScriptArgs[1].psz, "base"))
551 fBase = true;
552 else if (!RTStrICmp(paScriptArgs[1].psz, "diff"))
553 fBase = false;
554 else
555 {
556 RTPrintf("Invalid image mode '%s' given\n", paScriptArgs[1].psz);
557 rc = VERR_INVALID_PARAMETER;
558 }
559 pcszImage = paScriptArgs[2].psz;
560 if (!RTStrICmp(paScriptArgs[3].psz, "fixed"))
561 fDynamic = false;
562 else if (!RTStrICmp(paScriptArgs[3].psz, "dynamic"))
563 fDynamic = true;
564 else
565 {
566 RTPrintf("Invalid image type '%s' given\n", paScriptArgs[3].psz);
567 rc = VERR_INVALID_PARAMETER;
568 }
569 pcszBackend = paScriptArgs[4].psz;
570 cbSize = paScriptArgs[5].u64;
571 fIgnoreFlush = paScriptArgs[6].f;
572 fHonorSame = paScriptArgs[7].f;
573
574 if (RT_SUCCESS(rc))
575 {
576 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
577 if (pDisk)
578 {
579 unsigned fOpenFlags = VD_OPEN_FLAGS_ASYNC_IO;
580 unsigned fImageFlags = VD_IMAGE_FLAGS_NONE;
581
582 if (!fDynamic)
583 fImageFlags |= VD_IMAGE_FLAGS_FIXED;
584
585 if (fIgnoreFlush)
586 fOpenFlags |= VD_OPEN_FLAGS_IGNORE_FLUSH;
587
588 if (fHonorSame)
589 fOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
590
591 if (fBase)
592 rc = VDCreateBase(pDisk->pVD, pcszBackend, pcszImage, cbSize, fImageFlags, NULL,
593 &pDisk->PhysGeom, &pDisk->LogicalGeom,
594 NULL, fOpenFlags, pGlob->pInterfacesImages, NULL);
595 else
596 rc = VDCreateDiff(pDisk->pVD, pcszBackend, pcszImage, fImageFlags, NULL, NULL, NULL,
597 fOpenFlags, pGlob->pInterfacesImages, NULL);
598 }
599 else
600 rc = VERR_NOT_FOUND;
601 }
602
603 return rc;
604}
605
606static DECLCALLBACK(int) vdScriptHandlerOpen(PVDSCRIPTARG paScriptArgs, void *pvUser)
607{
608 int rc = VINF_SUCCESS;
609 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
610 const char *pcszBackend = NULL;
611 const char *pcszImage = NULL;
612 const char *pcszDisk = NULL;
613 PVDDISK pDisk = NULL;
614 bool fShareable = false;
615 bool fReadonly = false;
616 bool fAsyncIo = true;
617 bool fDiscard = false;
618 bool fIgnoreFlush = false;
619 bool fHonorSame = false;
620
621 pcszDisk = paScriptArgs[0].psz;
622 pcszImage = paScriptArgs[1].psz;
623 pcszBackend = paScriptArgs[2].psz;
624 fShareable = paScriptArgs[3].f;
625 fReadonly = paScriptArgs[4].f;
626 fAsyncIo = paScriptArgs[5].f;
627 fDiscard = paScriptArgs[6].f;
628 fIgnoreFlush = paScriptArgs[7].f;
629 fHonorSame = paScriptArgs[8].f;
630
631 if (RT_SUCCESS(rc))
632 {
633 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
634 if (pDisk)
635 {
636 unsigned fOpenFlags = 0;
637
638 if (fAsyncIo)
639 fOpenFlags |= VD_OPEN_FLAGS_ASYNC_IO;
640 if (fShareable)
641 fOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
642 if (fReadonly)
643 fOpenFlags |= VD_OPEN_FLAGS_READONLY;
644 if (fDiscard)
645 fOpenFlags |= VD_OPEN_FLAGS_DISCARD;
646 if (fIgnoreFlush)
647 fOpenFlags |= VD_OPEN_FLAGS_IGNORE_FLUSH;
648 if (fHonorSame)
649 fOpenFlags |= VD_OPEN_FLAGS_HONOR_SAME;
650
651 rc = VDOpen(pDisk->pVD, pcszBackend, pcszImage, fOpenFlags, pGlob->pInterfacesImages);
652 }
653 else
654 rc = VERR_NOT_FOUND;
655 }
656
657 return rc;
658}
659
660static DECLCALLBACK(int) vdScriptHandlerIo(PVDSCRIPTARG paScriptArgs, void *pvUser)
661{
662 int rc = VINF_SUCCESS;
663 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
664 bool fAsync = false;
665 bool fRandomAcc = false;
666 uint64_t cbIo = 0;
667 uint64_t cbBlkSize = 0;
668 bool fDataProviderRnd = false;
669 bool fPrintStats = false;
670 uint64_t offStart = 0;
671 uint64_t offEnd = 0;
672 unsigned cMaxReqs = 0;
673 uint8_t uWriteChance = 0;
674 const char *pcszDisk = NULL;
675 const char *pcszPattern = NULL;
676 PVDDISK pDisk = NULL;
677 PVDPATTERN pPattern = NULL;
678
679 pcszDisk = paScriptArgs[0].psz;
680 fAsync = paScriptArgs[1].f;
681 cMaxReqs = paScriptArgs[2].u64;
682 if (!RTStrICmp(paScriptArgs[3].psz, "seq"))
683 fRandomAcc = false;
684 else if (!RTStrICmp(paScriptArgs[3].psz, "rnd"))
685 fRandomAcc = true;
686 else
687 {
688 RTPrintf("Invalid access mode '%s'\n", paScriptArgs[3].psz);
689 rc = VERR_INVALID_PARAMETER;
690 }
691 cbBlkSize = paScriptArgs[4].u64;
692 offStart = paScriptArgs[5].u64;
693 offEnd = paScriptArgs[6].u64;
694 cbIo = paScriptArgs[7].u64;
695 uWriteChance = (uint8_t)paScriptArgs[8].u64;
696 pcszPattern = paScriptArgs[9].psz;
697
698 if ( RT_SUCCESS(rc)
699 && fAsync
700 && !cMaxReqs)
701 rc = VERR_INVALID_PARAMETER;
702
703 if (RT_SUCCESS(rc))
704 {
705 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
706 if (!pDisk)
707 rc = VERR_NOT_FOUND;
708 }
709
710 if (RT_SUCCESS(rc))
711 {
712 /* Set defaults if not set by the user. */
713 if (offStart == 0 && offEnd == 0)
714 {
715 offEnd = VDGetSize(pDisk->pVD, VD_LAST_IMAGE);
716 if (offEnd == 0)
717 return VERR_INVALID_STATE;
718 }
719
720 if (!cbIo)
721 cbIo = offEnd;
722 }
723
724 if ( RT_SUCCESS(rc)
725 && RTStrCmp(pcszPattern, "none"))
726 {
727 pPattern = tstVDIoGetPatternByName(pGlob, pcszPattern);
728 if (!pPattern)
729 rc = VERR_NOT_FOUND;
730 }
731
732 if (RT_SUCCESS(rc))
733 {
734 VDIOTEST IoTest;
735
736 RTTestSub(pGlob->hTest, "Basic I/O");
737 rc = tstVDIoTestInit(&IoTest, pGlob, fRandomAcc, cbIo, cbBlkSize, offStart, offEnd, uWriteChance, pPattern);
738 if (RT_SUCCESS(rc))
739 {
740 PVDIOREQ paIoReq = NULL;
741 unsigned cMaxTasksOutstanding = fAsync ? cMaxReqs : 1;
742 RTSEMEVENT EventSem;
743
744 rc = RTSemEventCreate(&EventSem);
745 paIoReq = (PVDIOREQ)RTMemAllocZ(cMaxTasksOutstanding * sizeof(VDIOREQ));
746 if (paIoReq && RT_SUCCESS(rc))
747 {
748 uint64_t NanoTS = RTTimeNanoTS();
749
750 /* Init requests. */
751 for (unsigned i = 0; i < cMaxTasksOutstanding; i++)
752 {
753 paIoReq[i].idx = i;
754 paIoReq[i].pvBufRead = RTMemAlloc(cbBlkSize);
755 if (!paIoReq[i].pvBufRead)
756 {
757 rc = VERR_NO_MEMORY;
758 break;
759 }
760 }
761
762 while ( tstVDIoTestRunning(&IoTest)
763 && RT_SUCCESS(rc))
764 {
765 bool fTasksOutstanding = false;
766 unsigned idx = 0;
767
768 /* Submit all idling requests. */
769 while ( idx < cMaxTasksOutstanding
770 && tstVDIoTestRunning(&IoTest))
771 {
772 if (!tstVDIoTestReqOutstanding(&paIoReq[idx]))
773 {
774 rc = tstVDIoTestReqInit(&IoTest, &paIoReq[idx], pDisk);
775 AssertRC(rc);
776
777 if (RT_SUCCESS(rc))
778 {
779 if (!fAsync)
780 {
781 switch (paIoReq[idx].enmTxDir)
782 {
783 case VDIOREQTXDIR_READ:
784 {
785 rc = VDRead(pDisk->pVD, paIoReq[idx].off, paIoReq[idx].DataSeg.pvSeg, paIoReq[idx].cbReq);
786
787 if (RT_SUCCESS(rc)
788 && pDisk->pMemDiskVerify)
789 {
790 RTSGBUF SgBuf;
791 RTSgBufInit(&SgBuf, &paIoReq[idx].DataSeg, 1);
792
793 if (VDMemDiskCmp(pDisk->pMemDiskVerify, paIoReq[idx].off, paIoReq[idx].cbReq, &SgBuf))
794 {
795 RTTestFailed(pGlob->hTest, "Corrupted disk at offset %llu!\n", paIoReq[idx].off);
796 rc = VERR_INVALID_STATE;
797 }
798 }
799 break;
800 }
801 case VDIOREQTXDIR_WRITE:
802 {
803 rc = VDWrite(pDisk->pVD, paIoReq[idx].off, paIoReq[idx].DataSeg.pvSeg, paIoReq[idx].cbReq);
804
805 if (RT_SUCCESS(rc)
806 && pDisk->pMemDiskVerify)
807 {
808 RTSGBUF SgBuf;
809 RTSgBufInit(&SgBuf, &paIoReq[idx].DataSeg, 1);
810 rc = VDMemDiskWrite(pDisk->pMemDiskVerify, paIoReq[idx].off, paIoReq[idx].cbReq, &SgBuf);
811 }
812 break;
813 }
814 case VDIOREQTXDIR_FLUSH:
815 {
816 rc = VDFlush(pDisk->pVD);
817 break;
818 }
819 case VDIOREQTXDIR_DISCARD:
820 AssertMsgFailed(("Invalid\n"));
821 }
822
823 ASMAtomicXchgBool(&paIoReq[idx].fOutstanding, false);
824 if (RT_SUCCESS(rc))
825 idx++;
826 }
827 else
828 {
829 LogFlow(("Queuing request %d\n", idx));
830 switch (paIoReq[idx].enmTxDir)
831 {
832 case VDIOREQTXDIR_READ:
833 {
834 rc = VDAsyncRead(pDisk->pVD, paIoReq[idx].off, paIoReq[idx].cbReq, &paIoReq[idx].SgBuf,
835 tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
836 break;
837 }
838 case VDIOREQTXDIR_WRITE:
839 {
840 rc = VDAsyncWrite(pDisk->pVD, paIoReq[idx].off, paIoReq[idx].cbReq, &paIoReq[idx].SgBuf,
841 tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
842 break;
843 }
844 case VDIOREQTXDIR_FLUSH:
845 {
846 rc = VDAsyncFlush(pDisk->pVD, tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
847 break;
848 }
849 case VDIOREQTXDIR_DISCARD:
850 AssertMsgFailed(("Invalid\n"));
851 }
852
853 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
854 {
855 idx++;
856 fTasksOutstanding = true;
857 rc = VINF_SUCCESS;
858 }
859 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
860 {
861 LogFlow(("Request %d completed\n", idx));
862 switch (paIoReq[idx].enmTxDir)
863 {
864 case VDIOREQTXDIR_READ:
865 {
866 if (pDisk->pMemDiskVerify)
867 {
868 RTCritSectEnter(&pDisk->CritSectVerify);
869 RTSgBufReset(&paIoReq[idx].SgBuf);
870
871 if (VDMemDiskCmp(pDisk->pMemDiskVerify, paIoReq[idx].off, paIoReq[idx].cbReq,
872 &paIoReq[idx].SgBuf))
873 {
874 RTTestFailed(pGlob->hTest, "Corrupted disk at offset %llu!\n", paIoReq[idx].off);
875 rc = VERR_INVALID_STATE;
876 }
877 RTCritSectLeave(&pDisk->CritSectVerify);
878 }
879 break;
880 }
881 case VDIOREQTXDIR_WRITE:
882 {
883 if (pDisk->pMemDiskVerify)
884 {
885 RTCritSectEnter(&pDisk->CritSectVerify);
886 RTSgBufReset(&paIoReq[idx].SgBuf);
887
888 rc = VDMemDiskWrite(pDisk->pMemDiskVerify, paIoReq[idx].off, paIoReq[idx].cbReq,
889 &paIoReq[idx].SgBuf);
890 RTCritSectLeave(&pDisk->CritSectVerify);
891 }
892 break;
893 }
894 case VDIOREQTXDIR_FLUSH:
895 break;
896 case VDIOREQTXDIR_DISCARD:
897 AssertMsgFailed(("Invalid\n"));
898 }
899
900 ASMAtomicXchgBool(&paIoReq[idx].fOutstanding, false);
901 if (rc != VERR_INVALID_STATE)
902 rc = VINF_SUCCESS;
903 }
904 }
905
906 if (RT_FAILURE(rc))
907 RTPrintf("Error submitting task %u rc=%Rrc\n", paIoReq[idx].idx, rc);
908 }
909 }
910 }
911
912 /* Wait for a request to complete. */
913 if ( fAsync
914 && fTasksOutstanding)
915 {
916 rc = RTSemEventWait(EventSem, RT_INDEFINITE_WAIT);
917 AssertRC(rc);
918 }
919 }
920
921 /* Cleanup, wait for all tasks to complete. */
922 while (fAsync)
923 {
924 unsigned idx = 0;
925 bool fAllIdle = true;
926
927 while (idx < cMaxTasksOutstanding)
928 {
929 if (tstVDIoTestReqOutstanding(&paIoReq[idx]))
930 {
931 fAllIdle = false;
932 break;
933 }
934 idx++;
935 }
936
937 if (!fAllIdle)
938 {
939 rc = RTSemEventWait(EventSem, 100);
940 Assert(RT_SUCCESS(rc) || rc == VERR_TIMEOUT);
941 }
942 else
943 break;
944 }
945
946 NanoTS = RTTimeNanoTS() - NanoTS;
947 uint64_t SpeedKBs = (uint64_t)(cbIo / (NanoTS / 1000000000.0) / 1024);
948 RTTestValue(pGlob->hTest, "Throughput", SpeedKBs, RTTESTUNIT_KILOBYTES_PER_SEC);
949
950 for (unsigned i = 0; i < cMaxTasksOutstanding; i++)
951 {
952 if (paIoReq[i].pvBufRead)
953 RTMemFree(paIoReq[i].pvBufRead);
954 }
955
956 RTSemEventDestroy(EventSem);
957 RTMemFree(paIoReq);
958 }
959 else
960 rc = VERR_NO_MEMORY;
961
962 tstVDIoTestDestroy(&IoTest);
963 }
964 }
965
966 return rc;
967}
968
969static DECLCALLBACK(int) vdScriptHandlerFlush(PVDSCRIPTARG paScriptArgs, void *pvUser)
970{
971 int rc = VINF_SUCCESS;
972 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
973 bool fAsync = false;
974 const char *pcszDisk = NULL;
975 PVDDISK pDisk = NULL;
976
977 pcszDisk = paScriptArgs[0].psz;
978 fAsync = paScriptArgs[1].f;
979
980 if (RT_SUCCESS(rc))
981 {
982 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
983 if (!pDisk)
984 rc = VERR_NOT_FOUND;
985 else if (fAsync)
986 {
987 VDIOREQ IoReq;
988 RTSEMEVENT EventSem;
989
990 rc = RTSemEventCreate(&EventSem);
991 if (RT_SUCCESS(rc))
992 {
993 memset(&IoReq, 0, sizeof(VDIOREQ));
994 IoReq.enmTxDir = VDIOREQTXDIR_FLUSH;
995 IoReq.pvUser = pDisk;
996 IoReq.idx = 0;
997 rc = VDAsyncFlush(pDisk->pVD, tstVDIoTestReqComplete, &IoReq, EventSem);
998 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
999 {
1000 rc = RTSemEventWait(EventSem, RT_INDEFINITE_WAIT);
1001 AssertRC(rc);
1002 }
1003 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
1004 rc = VINF_SUCCESS;
1005
1006 RTSemEventDestroy(EventSem);
1007 }
1008 }
1009 else
1010 rc = VDFlush(pDisk->pVD);
1011 }
1012
1013 return rc;
1014}
1015
1016static DECLCALLBACK(int) vdScriptHandlerMerge(PVDSCRIPTARG paScriptArgs, void *pvUser)
1017{
1018 int rc = VINF_SUCCESS;
1019 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1020 const char *pcszDisk = NULL;
1021 PVDDISK pDisk = NULL;
1022 unsigned nImageFrom = 0;
1023 unsigned nImageTo = 0;
1024
1025 pcszDisk = paScriptArgs[0].psz;
1026 nImageFrom = paScriptArgs[1].u32;
1027 nImageTo = paScriptArgs[2].u32;
1028
1029 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1030 if (!pDisk)
1031 rc = VERR_NOT_FOUND;
1032 else
1033 {
1034 /** @todo: Provide progress interface to test that cancelation
1035 * doesn't corrupt the data.
1036 */
1037 rc = VDMerge(pDisk->pVD, nImageFrom, nImageTo, NULL);
1038 }
1039
1040 return rc;
1041}
1042
1043static DECLCALLBACK(int) vdScriptHandlerCompact(PVDSCRIPTARG paScriptArgs, void *pvUser)
1044{
1045 int rc = VINF_SUCCESS;
1046 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1047 const char *pcszDisk = NULL;
1048 PVDDISK pDisk = NULL;
1049 unsigned nImage = 0;
1050
1051 pcszDisk = paScriptArgs[0].psz;
1052 nImage = paScriptArgs[1].u32;
1053
1054 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1055 if (!pDisk)
1056 rc = VERR_NOT_FOUND;
1057 else
1058 {
1059 /** @todo: Provide progress interface to test that cancelation
1060 * doesn't corrupt the data.
1061 */
1062 rc = VDCompact(pDisk->pVD, nImage, NULL);
1063 }
1064
1065 return rc;
1066}
1067
1068static DECLCALLBACK(int) vdScriptHandlerDiscard(PVDSCRIPTARG paScriptArgs, void *pvUser)
1069{
1070 int rc = VINF_SUCCESS;
1071 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1072 const char *pcszDisk = NULL;
1073 PVDDISK pDisk = NULL;
1074 bool fAsync = false;
1075 const char *pcszRanges = NULL;
1076
1077 pcszDisk = paScriptArgs[0].psz;
1078 fAsync = paScriptArgs[1].f;
1079 pcszRanges = paScriptArgs[2].psz;
1080
1081 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1082 if (!pDisk)
1083 rc = VERR_NOT_FOUND;
1084 else
1085 {
1086 unsigned cRanges = 0;
1087 PRTRANGE paRanges = NULL;
1088
1089 /*
1090 * Parse the range string which should look like this:
1091 * n,off1,cb1,off2,cb2,...
1092 *
1093 * <n> gives the number of ranges in the string and every off<i>,cb<i>
1094 * pair afterwards is a start offset + number of bytes to discard entry.
1095 */
1096 do
1097 {
1098 rc = RTStrToUInt32Ex(pcszRanges, (char **)&pcszRanges, 10, &cRanges);
1099 if (RT_FAILURE(rc) && (rc != VWRN_TRAILING_CHARS))
1100 break;
1101
1102 if (!cRanges)
1103 {
1104 rc = VERR_INVALID_PARAMETER;
1105 break;
1106 }
1107
1108 paRanges = (PRTRANGE)RTMemAllocZ(cRanges * sizeof(RTRANGE));
1109 if (!paRanges)
1110 {
1111 rc = VERR_NO_MEMORY;
1112 break;
1113 }
1114
1115 if (*pcszRanges != ',')
1116 {
1117 rc = VERR_INVALID_PARAMETER;
1118 break;
1119 }
1120
1121 pcszRanges++;
1122
1123 /* Retrieve each pair from the string. */
1124 for (unsigned i = 0; i < cRanges; i++)
1125 {
1126 uint64_t off;
1127 uint32_t cb;
1128
1129 rc = RTStrToUInt64Ex(pcszRanges, (char **)&pcszRanges, 10, &off);
1130 if (RT_FAILURE(rc) && (rc != VWRN_TRAILING_CHARS))
1131 break;
1132
1133 if (*pcszRanges != ',')
1134 {
1135 switch (*pcszRanges)
1136 {
1137 case 'k':
1138 case 'K':
1139 {
1140 off *= _1K;
1141 break;
1142 }
1143 case 'm':
1144 case 'M':
1145 {
1146 off *= _1M;
1147 break;
1148 }
1149 case 'g':
1150 case 'G':
1151 {
1152 off *= _1G;
1153 break;
1154 }
1155 default:
1156 {
1157 RTPrintf("Invalid size suffix '%s'\n", pcszRanges);
1158 rc = VERR_INVALID_PARAMETER;
1159 }
1160 }
1161 if (RT_SUCCESS(rc))
1162 pcszRanges++;
1163 }
1164
1165 if (*pcszRanges != ',')
1166 {
1167 rc = VERR_INVALID_PARAMETER;
1168 break;
1169 }
1170
1171 pcszRanges++;
1172
1173 rc = RTStrToUInt32Ex(pcszRanges, (char **)&pcszRanges, 10, &cb);
1174 if (RT_FAILURE(rc) && (rc != VWRN_TRAILING_CHARS))
1175 break;
1176
1177 if (*pcszRanges != ',')
1178 {
1179 switch (*pcszRanges)
1180 {
1181 case 'k':
1182 case 'K':
1183 {
1184 cb *= _1K;
1185 break;
1186 }
1187 case 'm':
1188 case 'M':
1189 {
1190 cb *= _1M;
1191 break;
1192 }
1193 case 'g':
1194 case 'G':
1195 {
1196 cb *= _1G;
1197 break;
1198 }
1199 default:
1200 {
1201 RTPrintf("Invalid size suffix '%s'\n", pcszRanges);
1202 rc = VERR_INVALID_PARAMETER;
1203 }
1204 }
1205 if (RT_SUCCESS(rc))
1206 pcszRanges++;
1207 }
1208
1209 if ( *pcszRanges != ','
1210 && !(i == cRanges - 1 && *pcszRanges == '\0'))
1211 {
1212 rc = VERR_INVALID_PARAMETER;
1213 break;
1214 }
1215
1216 pcszRanges++;
1217
1218 paRanges[i].offStart = off;
1219 paRanges[i].cbRange = cb;
1220 }
1221 } while (0);
1222
1223 if (RT_SUCCESS(rc))
1224 {
1225 if (!fAsync)
1226 rc = VDDiscardRanges(pDisk->pVD, paRanges, cRanges);
1227 else
1228 {
1229 VDIOREQ IoReq;
1230 RTSEMEVENT EventSem;
1231
1232 rc = RTSemEventCreate(&EventSem);
1233 if (RT_SUCCESS(rc))
1234 {
1235 memset(&IoReq, 0, sizeof(VDIOREQ));
1236 IoReq.enmTxDir = VDIOREQTXDIR_FLUSH;
1237 IoReq.pvUser = pDisk;
1238 IoReq.idx = 0;
1239 rc = VDAsyncDiscardRanges(pDisk->pVD, paRanges, cRanges, tstVDIoTestReqComplete, &IoReq, EventSem);
1240 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
1241 {
1242 rc = RTSemEventWait(EventSem, RT_INDEFINITE_WAIT);
1243 AssertRC(rc);
1244 }
1245 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
1246 rc = VINF_SUCCESS;
1247
1248 RTSemEventDestroy(EventSem);
1249 }
1250 }
1251
1252 if ( RT_SUCCESS(rc)
1253 && pDisk->pMemDiskVerify)
1254 {
1255 for (unsigned i = 0; i < cRanges; i++)
1256 {
1257 void *pv = RTMemAllocZ(paRanges[i].cbRange);
1258 if (pv)
1259 {
1260 RTSGSEG SgSeg;
1261 RTSGBUF SgBuf;
1262
1263 SgSeg.pvSeg = pv;
1264 SgSeg.cbSeg = paRanges[i].cbRange;
1265 RTSgBufInit(&SgBuf, &SgSeg, 1);
1266 rc = VDMemDiskWrite(pDisk->pMemDiskVerify, paRanges[i].offStart, paRanges[i].cbRange, &SgBuf);
1267 RTMemFree(pv);
1268 }
1269 else
1270 {
1271 rc = VERR_NO_MEMORY;
1272 break;
1273 }
1274 }
1275 }
1276 }
1277
1278 if (paRanges)
1279 RTMemFree(paRanges);
1280 }
1281
1282 return rc;
1283}
1284
1285static DECLCALLBACK(int) vdScriptHandlerCopy(PVDSCRIPTARG paScriptArgs, void *pvUser)
1286{
1287 int rc = VINF_SUCCESS;
1288 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1289 const char *pcszDiskFrom = NULL;
1290 const char *pcszDiskTo = NULL;
1291 PVDDISK pDiskFrom = NULL;
1292 PVDDISK pDiskTo = NULL;
1293 unsigned nImageFrom = 0;
1294 const char *pcszBackend = NULL;
1295 const char *pcszFilename = NULL;
1296 bool fMoveByRename = false;
1297 uint64_t cbSize = 0;
1298 unsigned nImageFromSame = VD_IMAGE_CONTENT_UNKNOWN;
1299 unsigned nImageToSame = VD_IMAGE_CONTENT_UNKNOWN;
1300
1301 pcszDiskFrom = paScriptArgs[0].psz;
1302 pcszDiskTo = paScriptArgs[1].psz;
1303 nImageFrom = paScriptArgs[2].u32;
1304 pcszBackend = paScriptArgs[3].psz;
1305 pcszFilename = paScriptArgs[4].psz;
1306 fMoveByRename = paScriptArgs[5].f;
1307 cbSize = paScriptArgs[6].u64;
1308 nImageFromSame = paScriptArgs[7].u32;
1309 nImageToSame = paScriptArgs[8].u32;
1310
1311 pDiskFrom = tstVDIoGetDiskByName(pGlob, pcszDiskFrom);
1312 pDiskTo = tstVDIoGetDiskByName(pGlob, pcszDiskTo);
1313 if (!pDiskFrom || !pDiskTo)
1314 rc = VERR_NOT_FOUND;
1315 else
1316 {
1317 /** @todo: Provide progress interface to test that cancelation
1318 * works as intended.
1319 */
1320 rc = VDCopyEx(pDiskFrom->pVD, nImageFrom, pDiskTo->pVD, pcszBackend, pcszFilename,
1321 fMoveByRename, cbSize, nImageFromSame, nImageToSame,
1322 VD_IMAGE_FLAGS_NONE, NULL, VD_OPEN_FLAGS_ASYNC_IO,
1323 NULL, pGlob->pInterfacesImages, NULL);
1324 }
1325
1326 return rc;
1327}
1328
1329static DECLCALLBACK(int) vdScriptHandlerClose(PVDSCRIPTARG paScriptArgs, void *pvUser)
1330{
1331 int rc = VINF_SUCCESS;
1332 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1333 bool fAll = false;
1334 bool fDelete = false;
1335 const char *pcszDisk = NULL;
1336 PVDDISK pDisk = NULL;
1337
1338 pcszDisk = paScriptArgs[0].psz;
1339 if (!RTStrICmp(paScriptArgs[1].psz, "all"))
1340 fAll = true;
1341 else if (!RTStrICmp(paScriptArgs[1].psz, "single"))
1342 fAll = false;
1343 else
1344 {
1345 RTPrintf("Invalid mode '%s' given\n", paScriptArgs[1].psz);
1346 rc = VERR_INVALID_PARAMETER;
1347 }
1348 fDelete = paScriptArgs[2].f;
1349
1350 if ( fAll
1351 && fDelete)
1352 {
1353 RTPrintf("mode=all doesn't work with delete=yes\n");
1354 rc = VERR_INVALID_PARAMETER;
1355 }
1356
1357 if (RT_SUCCESS(rc))
1358 {
1359 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1360 if (pDisk)
1361 {
1362 if (fAll)
1363 rc = VDCloseAll(pDisk->pVD);
1364 else
1365 rc = VDClose(pDisk->pVD, fDelete);
1366 }
1367 else
1368 rc = VERR_NOT_FOUND;
1369 }
1370 return rc;
1371}
1372
1373
1374static DECLCALLBACK(int) vdScriptHandlerPrintFileSize(PVDSCRIPTARG paScriptArgs, void *pvUser)
1375{
1376 int rc = VINF_SUCCESS;
1377 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1378 const char *pcszDisk = NULL;
1379 PVDDISK pDisk = NULL;
1380 unsigned nImage = 0;
1381
1382 pcszDisk = paScriptArgs[0].psz;
1383 nImage = paScriptArgs[1].u32;
1384
1385 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1386 if (pDisk)
1387 RTPrintf("%s: size of image %u is %llu\n", pcszDisk, nImage, VDGetFileSize(pDisk->pVD, nImage));
1388 else
1389 rc = VERR_NOT_FOUND;
1390
1391 return rc;
1392}
1393
1394
1395static DECLCALLBACK(int) vdScriptHandlerIoLogReplay(PVDSCRIPTARG paScriptArgs, void *pvUser)
1396{
1397 int rc = VINF_SUCCESS;
1398 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1399 const char *pcszDisk = NULL;
1400 PVDDISK pDisk = NULL;
1401 const char *pcszIoLog = NULL;
1402
1403 pcszDisk = paScriptArgs[0].psz;
1404 pcszIoLog = paScriptArgs[1].psz;
1405
1406 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1407 if (pDisk)
1408 {
1409 VDIOLOGGER hIoLogger;
1410
1411 rc = VDDbgIoLogOpen(&hIoLogger, pcszIoLog);
1412 if (RT_SUCCESS(rc))
1413 {
1414 uint32_t fIoLogFlags;
1415 VDIOLOGEVENT enmEvent;
1416 void *pvBuf = NULL;
1417 size_t cbBuf = 0;
1418
1419 fIoLogFlags = VDDbgIoLogGetFlags(hIoLogger);
1420
1421 /* Loop through events. */
1422 rc = VDDbgIoLogEventTypeGetNext(hIoLogger, &enmEvent);
1423 while ( RT_SUCCESS(rc)
1424 && enmEvent != VDIOLOGEVENT_END)
1425 {
1426 VDDBGIOLOGREQ enmReq = VDDBGIOLOGREQ_INVALID;
1427 uint64_t idEvent = 0;
1428 bool fAsync = false;
1429 uint64_t off = 0;
1430 size_t cbIo = 0;
1431 Assert(enmEvent == VDIOLOGEVENT_START);
1432
1433 rc = VDDbgIoLogReqTypeGetNext(hIoLogger, &enmReq);
1434 if (RT_FAILURE(rc))
1435 break;
1436
1437 switch (enmReq)
1438 {
1439 case VDDBGIOLOGREQ_READ:
1440 {
1441 rc = VDDbgIoLogEventGetStart(hIoLogger, &idEvent, &fAsync,
1442 &off, &cbIo, 0, NULL);
1443 if ( RT_SUCCESS(rc)
1444 && cbIo > cbBuf)
1445 {
1446 pvBuf = RTMemRealloc(pvBuf, cbIo);
1447 if (pvBuf)
1448 cbBuf = cbIo;
1449 else
1450 rc = VERR_NO_MEMORY;
1451 }
1452
1453 if ( RT_SUCCESS(rc)
1454 && !fAsync)
1455 rc = VDRead(pDisk->pVD, off, pvBuf, cbIo);
1456 else if (RT_SUCCESS(rc))
1457 rc = VERR_NOT_SUPPORTED;
1458 break;
1459 }
1460 case VDDBGIOLOGREQ_WRITE:
1461 {
1462 rc = VDDbgIoLogEventGetStart(hIoLogger, &idEvent, &fAsync,
1463 &off, &cbIo, cbBuf, pvBuf);
1464 if (rc == VERR_BUFFER_OVERFLOW)
1465 {
1466 pvBuf = RTMemRealloc(pvBuf, cbIo);
1467 if (pvBuf)
1468 {
1469 cbBuf = cbIo;
1470 rc = VDDbgIoLogEventGetStart(hIoLogger, &idEvent, &fAsync,
1471 &off, &cbIo, cbBuf, pvBuf);
1472 }
1473 else
1474 rc = VERR_NO_MEMORY;
1475 }
1476
1477 if ( RT_SUCCESS(rc)
1478 && !fAsync)
1479 rc = VDWrite(pDisk->pVD, off, pvBuf, cbIo);
1480 else if (RT_SUCCESS(rc))
1481 rc = VERR_NOT_SUPPORTED;
1482 break;
1483 }
1484 case VDDBGIOLOGREQ_FLUSH:
1485 {
1486 rc = VDDbgIoLogEventGetStart(hIoLogger, &idEvent, &fAsync,
1487 &off, &cbIo, 0, NULL);
1488 if ( RT_SUCCESS(rc)
1489 && !fAsync)
1490 rc = VDFlush(pDisk->pVD);
1491 else if (RT_SUCCESS(rc))
1492 rc = VERR_NOT_SUPPORTED;
1493 break;
1494 }
1495 case VDDBGIOLOGREQ_DISCARD:
1496 {
1497 PRTRANGE paRanges = NULL;
1498 unsigned cRanges = 0;
1499
1500 rc = VDDbgIoLogEventGetStartDiscard(hIoLogger, &idEvent, &fAsync,
1501 &paRanges, &cRanges);
1502 if ( RT_SUCCESS(rc)
1503 && !fAsync)
1504 {
1505 rc = VDDiscardRanges(pDisk->pVD, paRanges, cRanges);
1506 RTMemFree(paRanges);
1507 }
1508 else if (RT_SUCCESS(rc))
1509 rc = VERR_NOT_SUPPORTED;
1510 break;
1511 }
1512 default:
1513 AssertMsgFailed(("Invalid request type %d\n", enmReq));
1514 }
1515
1516 if (RT_SUCCESS(rc))
1517 {
1518 /* Get matching complete event. */
1519 rc = VDDbgIoLogEventTypeGetNext(hIoLogger, &enmEvent);
1520 if (RT_SUCCESS(rc))
1521 {
1522 uint64_t idEvtComplete;
1523 int rcReq;
1524 uint64_t msDuration;
1525
1526 Assert(enmEvent == VDIOLOGEVENT_COMPLETE);
1527 rc = VDDbgIoLogEventGetComplete(hIoLogger, &idEvtComplete, &rcReq,
1528 &msDuration, &cbIo, cbBuf, pvBuf);
1529 Assert(RT_FAILURE(rc) || idEvtComplete == idEvent);
1530 }
1531 }
1532
1533 if (RT_SUCCESS(rc))
1534 rc = VDDbgIoLogEventTypeGetNext(hIoLogger, &enmEvent);
1535 }
1536
1537 VDDbgIoLogDestroy(hIoLogger);
1538 }
1539 }
1540 else
1541 rc = VERR_NOT_FOUND;
1542
1543 return rc;
1544}
1545
1546
1547static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDSCRIPTARG paScriptArgs, void *pvUser)
1548{
1549 int rc = VINF_SUCCESS;
1550 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1551 size_t cbPattern = 0;
1552 uint64_t uSeed = 0;
1553 const char *pcszSeeder = NULL;
1554
1555 cbPattern = paScriptArgs[0].u64;
1556 pcszSeeder = paScriptArgs[1].psz;
1557 uSeed = paScriptArgs[2].u64;
1558
1559 if (pGlob->pIoRnd)
1560 {
1561 RTPrintf("I/O RNG already exists\n");
1562 rc = VERR_INVALID_STATE;
1563 }
1564 else
1565 {
1566 uint64_t uSeedToUse = 0;
1567
1568 if (!RTStrICmp(pcszSeeder, "manual"))
1569 uSeedToUse = uSeed;
1570 else if (!RTStrICmp(pcszSeeder, "time"))
1571 uSeedToUse = RTTimeSystemMilliTS();
1572 else if (!RTStrICmp(pcszSeeder, "system"))
1573 {
1574 RTRAND hRand;
1575 rc = RTRandAdvCreateSystemTruer(&hRand);
1576 if (RT_SUCCESS(rc))
1577 {
1578 RTRandAdvBytes(hRand, &uSeedToUse, sizeof(uSeedToUse));
1579 RTRandAdvDestroy(hRand);
1580 }
1581 }
1582
1583 if (RT_SUCCESS(rc))
1584 rc = VDIoRndCreate(&pGlob->pIoRnd, cbPattern, uSeed);
1585 }
1586
1587 return rc;
1588}
1589
1590static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser)
1591{
1592 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1593
1594 if (pGlob->pIoRnd)
1595 {
1596 VDIoRndDestroy(pGlob->pIoRnd);
1597 pGlob->pIoRnd = NULL;
1598 }
1599 else
1600 RTPrintf("WARNING: No I/O RNG active, faulty script. Continuing\n");
1601
1602 return VINF_SUCCESS;
1603}
1604
1605static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromNumber(PVDSCRIPTARG paScriptArgs, void *pvUser)
1606{
1607 int rc = VINF_SUCCESS;
1608 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1609 size_t cbPattern = 0;
1610 const char *pcszName = NULL;
1611 uint64_t u64Pattern = 0;
1612
1613 pcszName = paScriptArgs[0].psz;
1614 cbPattern = paScriptArgs[1].u64;
1615 u64Pattern = paScriptArgs[2].u64;
1616
1617 PVDPATTERN pPattern = tstVDIoGetPatternByName(pGlob, pcszName);
1618 if (!pPattern)
1619 {
1620 pPattern = tstVDIoPatternCreate(pcszName, RT_ALIGN_Z(cbPattern, sizeof(uint64_t)));
1621 if (pPattern)
1622 {
1623 /* Fill the buffer. */
1624 void *pv = pPattern->pvPattern;
1625
1626 while (pPattern->cbPattern > 0)
1627 {
1628 *((uint64_t*)pv) = u64Pattern;
1629 pPattern->cbPattern -= sizeof(uint64_t);
1630 pv = (uint64_t *)pv + 1;
1631 }
1632 pPattern->cbPattern = cbPattern; /* Set to the desired size. (could be unaligned) */
1633
1634 RTListAppend(&pGlob->ListPatterns, &pPattern->ListNode);
1635 }
1636 else
1637 rc = VERR_NO_MEMORY;
1638 }
1639 else
1640 rc = VERR_ALREADY_EXISTS;
1641
1642 return rc;
1643}
1644
1645static DECLCALLBACK(int) vdScriptHandlerIoPatternCreateFromFile(PVDSCRIPTARG paScriptArgs, void *pvUser)
1646{
1647 int rc = VINF_SUCCESS;
1648 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1649 const char *pcszName = NULL;
1650 const char *pcszFile = NULL;
1651
1652 pcszName = paScriptArgs[0].psz;
1653 pcszFile = paScriptArgs[1].psz;
1654
1655 PVDPATTERN pPattern = tstVDIoGetPatternByName(pGlob, pcszName);
1656 if (!pPattern)
1657 {
1658 RTFILE hFile;
1659 uint64_t cbPattern = 0;
1660
1661 rc = RTFileOpen(&hFile, pcszFile, RTFILE_O_DENY_NONE | RTFILE_O_OPEN | RTFILE_O_READ);
1662 if (RT_SUCCESS(rc))
1663 {
1664 rc = RTFileGetSize(hFile, &cbPattern);
1665 if (RT_SUCCESS(rc))
1666 {
1667 pPattern = tstVDIoPatternCreate(pcszName, (size_t)cbPattern);
1668 if (pPattern)
1669 {
1670 rc = RTFileRead(hFile, pPattern->pvPattern, (size_t)cbPattern, NULL);
1671 if (RT_SUCCESS(rc))
1672 RTListAppend(&pGlob->ListPatterns, &pPattern->ListNode);
1673 else
1674 {
1675 RTMemFree(pPattern->pvPattern);
1676 RTStrFree(pPattern->pszName);
1677 RTMemFree(pPattern);
1678 }
1679 }
1680 else
1681 rc = VERR_NO_MEMORY;
1682 }
1683 RTFileClose(hFile);
1684 }
1685 }
1686 else
1687 rc = VERR_ALREADY_EXISTS;
1688
1689 return rc;
1690}
1691
1692static DECLCALLBACK(int) vdScriptHandlerIoPatternDestroy(PVDSCRIPTARG paScriptArgs, void *pvUser)
1693{
1694 int rc = VINF_SUCCESS;
1695 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1696 const char *pcszName = NULL;
1697
1698 pcszName = paScriptArgs[0].psz;
1699
1700 PVDPATTERN pPattern = tstVDIoGetPatternByName(pGlob, pcszName);
1701 if (pPattern)
1702 {
1703 RTListNodeRemove(&pPattern->ListNode);
1704 RTMemFree(pPattern->pvPattern);
1705 RTStrFree(pPattern->pszName);
1706 RTMemFree(pPattern);
1707 }
1708 else
1709 rc = VERR_NOT_FOUND;
1710
1711 return rc;
1712}
1713
1714static DECLCALLBACK(int) vdScriptHandlerSleep(PVDSCRIPTARG paScriptArgs, void *pvUser)
1715{
1716 int rc = VINF_SUCCESS;
1717 uint64_t cMillies = paScriptArgs[0].u64;
1718
1719 rc = RTThreadSleep(cMillies);
1720 return rc;
1721}
1722
1723static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDSCRIPTARG paScriptArgs, void *pvUser)
1724{
1725 int rc = VINF_SUCCESS;
1726 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1727 const char *pcszFile = NULL;
1728 const char *pcszPathToDump = NULL;
1729
1730 pcszFile = paScriptArgs[0].psz;
1731 pcszPathToDump = paScriptArgs[1].psz;
1732
1733 /* Check for the file. */
1734 PVDFILE pIt = NULL;
1735 bool fFound = false;
1736 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1737 {
1738 if (!RTStrCmp(pIt->pszName, pcszFile))
1739 {
1740 fFound = true;
1741 break;
1742 }
1743 }
1744
1745 if (fFound)
1746 {
1747 RTPrintf("Dumping memory file %s to %s, this might take some time\n", pcszFile, pcszPathToDump);
1748 //rc = VDMemDiskWriteToFile(pIt->pIo, pcszPathToDump);
1749 rc = VERR_NOT_IMPLEMENTED;
1750 }
1751 else
1752 rc = VERR_FILE_NOT_FOUND;
1753
1754 return rc;
1755}
1756
1757static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDSCRIPTARG paScriptArgs, void *pvUser)
1758{
1759 int rc = VINF_SUCCESS;
1760 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1761 const char *pcszDisk = NULL;
1762 PVDDISK pDisk = NULL;
1763 bool fVerify = false;
1764
1765 pcszDisk = paScriptArgs[0].psz;
1766 fVerify = paScriptArgs[1].f;
1767
1768 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1769 if (pDisk)
1770 rc = VERR_ALREADY_EXISTS;
1771 else
1772 {
1773 pDisk = (PVDDISK)RTMemAllocZ(sizeof(VDDISK));
1774 if (pDisk)
1775 {
1776 pDisk->pTestGlob = pGlob;
1777 pDisk->pszName = RTStrDup(pcszDisk);
1778 if (pDisk->pszName)
1779 {
1780 rc = VINF_SUCCESS;
1781
1782 if (fVerify)
1783 {
1784 rc = VDMemDiskCreate(&pDisk->pMemDiskVerify, 0 /* Growing */);
1785 if (RT_SUCCESS(rc))
1786 {
1787 rc = RTCritSectInit(&pDisk->CritSectVerify);
1788 if (RT_FAILURE(rc))
1789 VDMemDiskDestroy(pDisk->pMemDiskVerify);
1790 }
1791 }
1792
1793 if (RT_SUCCESS(rc))
1794 {
1795 rc = VDCreate(pGlob->pInterfacesDisk, VDTYPE_HDD, &pDisk->pVD);
1796
1797 if (RT_SUCCESS(rc))
1798 RTListAppend(&pGlob->ListDisks, &pDisk->ListNode);
1799 else
1800 {
1801 if (fVerify)
1802 {
1803 RTCritSectDelete(&pDisk->CritSectVerify);
1804 VDMemDiskDestroy(pDisk->pMemDiskVerify);
1805 }
1806 RTStrFree(pDisk->pszName);
1807 }
1808 }
1809 }
1810 else
1811 rc = VERR_NO_MEMORY;
1812
1813 if (RT_FAILURE(rc))
1814 RTMemFree(pDisk);
1815 }
1816 else
1817 rc = VERR_NO_MEMORY;
1818 }
1819 return rc;
1820}
1821
1822static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDSCRIPTARG paScriptArgs, void *pvUser)
1823{
1824 int rc = VINF_SUCCESS;
1825 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1826 const char *pcszDisk = NULL;
1827 PVDDISK pDisk = NULL;
1828
1829 pcszDisk = paScriptArgs[0].psz;
1830
1831 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1832 if (pDisk)
1833 {
1834 RTListNodeRemove(&pDisk->ListNode);
1835 VDDestroy(pDisk->pVD);
1836 if (pDisk->pMemDiskVerify)
1837 {
1838 VDMemDiskDestroy(pDisk->pMemDiskVerify);
1839 RTCritSectDelete(&pDisk->CritSectVerify);
1840 }
1841 RTStrFree(pDisk->pszName);
1842 RTMemFree(pDisk);
1843 }
1844 else
1845 rc = VERR_NOT_FOUND;
1846
1847 return rc;
1848}
1849
1850static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDSCRIPTARG paScriptArgs, void *pvUser)
1851{
1852 int rc = VINF_SUCCESS;
1853 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1854 const char *pcszDisk1 = NULL;
1855 PVDDISK pDisk1 = NULL;
1856 const char *pcszDisk2 = NULL;
1857 PVDDISK pDisk2 = NULL;
1858
1859 pcszDisk1 = paScriptArgs[0].psz;
1860 pcszDisk2 = paScriptArgs[1].psz;
1861
1862 pDisk1 = tstVDIoGetDiskByName(pGlob, pcszDisk1);
1863 pDisk2 = tstVDIoGetDiskByName(pGlob, pcszDisk2);
1864
1865 if (pDisk1 && pDisk2)
1866 {
1867 uint8_t *pbBuf1 = (uint8_t *)RTMemAllocZ(16 * _1M);
1868 uint8_t *pbBuf2 = (uint8_t *)RTMemAllocZ(16 * _1M);
1869 if (pbBuf1 && pbBuf2)
1870 {
1871 uint64_t cbDisk1, cbDisk2;
1872 uint64_t uOffCur = 0;
1873
1874 cbDisk1 = VDGetSize(pDisk1->pVD, VD_LAST_IMAGE);
1875 cbDisk2 = VDGetSize(pDisk2->pVD, VD_LAST_IMAGE);
1876
1877 RTTestSub(pGlob->hTest, "Comparing two disks for equal content");
1878 if (cbDisk1 != cbDisk2)
1879 RTTestFailed(pGlob->hTest, "Disks differ in size %llu vs %llu\n", cbDisk1, cbDisk2);
1880 else
1881 {
1882 while (uOffCur < cbDisk1)
1883 {
1884 size_t cbRead = RT_MIN(cbDisk1, 16 * _1M);
1885
1886 rc = VDRead(pDisk1->pVD, uOffCur, pbBuf1, cbRead);
1887 if (RT_SUCCESS(rc))
1888 rc = VDRead(pDisk2->pVD, uOffCur, pbBuf2, cbRead);
1889
1890 if (RT_SUCCESS(rc))
1891 {
1892 if (memcmp(pbBuf1, pbBuf2, cbRead))
1893 {
1894 RTTestFailed(pGlob->hTest, "Disks differ at offset %llu\n", uOffCur);
1895 rc = VERR_DEV_IO_ERROR;
1896 break;
1897 }
1898 }
1899 else
1900 {
1901 RTTestFailed(pGlob->hTest, "Reading one disk at offset %llu failed\n", uOffCur);
1902 break;
1903 }
1904
1905 uOffCur += cbRead;
1906 cbDisk1 -= cbRead;
1907 }
1908 }
1909 RTMemFree(pbBuf1);
1910 RTMemFree(pbBuf2);
1911 }
1912 else
1913 {
1914 if (pbBuf1)
1915 RTMemFree(pbBuf1);
1916 if (pbBuf2)
1917 RTMemFree(pbBuf2);
1918 rc = VERR_NO_MEMORY;
1919 }
1920 }
1921 else
1922 rc = VERR_NOT_FOUND;
1923
1924 return rc;
1925}
1926
1927static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDSCRIPTARG paScriptArgs, void *pvUser)
1928{
1929 int rc = VINF_SUCCESS;
1930 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1931 const char *pcszDisk = NULL;
1932 PVDDISK pDisk = NULL;
1933
1934 pcszDisk = paScriptArgs[0].psz;
1935
1936 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1937
1938 if (pDisk)
1939 VDDumpImages(pDisk->pVD);
1940 else
1941 rc = VERR_NOT_FOUND;
1942
1943 return rc;
1944}
1945
1946static DECLCALLBACK(int) vdScriptHandlerPrintMsg(PVDSCRIPTARG paScriptArgs, void *pvUser)
1947{
1948 RTPrintf("%s\n", paScriptArgs[0].psz);
1949 return VINF_SUCCESS;
1950}
1951
1952static DECLCALLBACK(int) vdScriptHandlerShowStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser)
1953{
1954 int rc = VINF_SUCCESS;
1955 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1956 const char *pcszFile = paScriptArgs[0].psz;
1957
1958 /* Check for the file. */
1959 PVDFILE pIt = NULL;
1960 bool fFound = false;
1961 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1962 {
1963 if (!RTStrCmp(pIt->pszName, pcszFile))
1964 {
1965 fFound = true;
1966 break;
1967 }
1968 }
1969
1970 if (fFound)
1971 {
1972 RTPrintf("Statistics %s: \n"
1973 " sync reads=%u writes=%u flushes=%u\n"
1974 " async reads=%u writes=%u flushes=%u\n",
1975 pcszFile,
1976 pIt->cReads, pIt->cWrites, pIt->cFlushes,
1977 pIt->cAsyncReads, pIt->cAsyncWrites, pIt->cAsyncFlushes);
1978 }
1979 else
1980 rc = VERR_FILE_NOT_FOUND;
1981
1982 return rc;
1983}
1984
1985static DECLCALLBACK(int) vdScriptHandlerResetStatistics(PVDSCRIPTARG paScriptArgs, void *pvUser)
1986{
1987 int rc = VINF_SUCCESS;
1988 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1989 const char *pcszFile = paScriptArgs[0].psz;
1990
1991 /* Check for the file. */
1992 PVDFILE pIt = NULL;
1993 bool fFound = false;
1994 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1995 {
1996 if (!RTStrCmp(pIt->pszName, pcszFile))
1997 {
1998 fFound = true;
1999 break;
2000 }
2001 }
2002
2003 if (fFound)
2004 {
2005 pIt->cReads = 0;
2006 pIt->cWrites = 0;
2007 pIt->cFlushes = 0;
2008
2009 pIt->cAsyncReads = 0;
2010 pIt->cAsyncWrites = 0;
2011 pIt->cAsyncFlushes = 0;
2012 }
2013 else
2014 rc = VERR_FILE_NOT_FOUND;
2015
2016 return rc;
2017}
2018
2019static DECLCALLBACK(int) vdScriptHandlerResize(PVDSCRIPTARG paScriptArgs, void *pvUser)
2020{
2021 int rc = VINF_SUCCESS;
2022 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2023 const char *pcszDisk = paScriptArgs[0].psz;
2024 uint64_t cbDiskNew = 0;
2025 PVDDISK pDisk = NULL;
2026
2027 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
2028 if (pDisk)
2029 {
2030 rc = VDResize(pDisk->pVD, cbDiskNew, &pDisk->PhysGeom, &pDisk->LogicalGeom, NULL);
2031 }
2032 else
2033 rc = VERR_NOT_FOUND;
2034
2035 return rc;
2036}
2037
2038static DECLCALLBACK(int) vdScriptHandlerSetFileBackend(PVDSCRIPTARG paScriptArgs, void *pvUser)
2039{
2040 int rc = VINF_SUCCESS;
2041 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2042 const char *pcszBackend = paScriptArgs[0].psz;
2043
2044 RTStrFree(pGlob->pszIoBackend);
2045 pGlob->pszIoBackend = RTStrDup(pcszBackend);
2046 if (!pGlob->pszIoBackend)
2047 rc = VERR_NO_MEMORY;
2048
2049 return rc;
2050}
2051
2052static DECLCALLBACK(int) tstVDIoFileOpen(void *pvUser, const char *pszLocation,
2053 uint32_t fOpen,
2054 PFNVDCOMPLETED pfnCompleted,
2055 void **ppStorage)
2056{
2057 int rc = VINF_SUCCESS;
2058 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2059 bool fFound = false;
2060
2061 /*
2062 * Some backends use ./ for paths, strip it.
2063 * @todo: Implement proper directory support for the
2064 * memory filesystem.
2065 */
2066 if ( strlen(pszLocation) >= 2
2067 && *pszLocation == '.'
2068 && pszLocation[1] == '/')
2069 pszLocation += 2;
2070
2071 /* Check if the file exists. */
2072 PVDFILE pIt = NULL;
2073 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
2074 {
2075 if (!RTStrCmp(pIt->pszName, pszLocation))
2076 {
2077 fFound = true;
2078 break;
2079 }
2080 }
2081
2082 if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE)
2083 {
2084 /* If the file exists delete the memory disk. */
2085 if (fFound)
2086 rc = VDIoBackendStorageSetSize(pIt->pIoStorage, 0);
2087 else
2088 {
2089 /* Create completey new. */
2090 pIt = (PVDFILE)RTMemAllocZ(sizeof(VDFILE));
2091 if (pIt)
2092 {
2093 pIt->pszName = RTStrDup(pszLocation);
2094
2095 if (pIt->pszName)
2096 {
2097 rc = VDIoBackendStorageCreate(pGlob->pIoBackend, pGlob->pszIoBackend,
2098 pszLocation, pfnCompleted, &pIt->pIoStorage);
2099 }
2100 else
2101 rc = VERR_NO_MEMORY;
2102
2103 if (RT_FAILURE(rc))
2104 {
2105 if (pIt->pszName)
2106 RTStrFree(pIt->pszName);
2107 RTMemFree(pIt);
2108 }
2109 else
2110 RTListAppend(&pGlob->ListFiles, &pIt->Node);
2111 }
2112 else
2113 rc = VERR_NO_MEMORY;
2114 }
2115 }
2116 else if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN)
2117 {
2118 if (!fFound)
2119 rc = VERR_FILE_NOT_FOUND;
2120 }
2121 else
2122 rc = VERR_INVALID_PARAMETER;
2123
2124 if (RT_SUCCESS(rc))
2125 {
2126 AssertPtr(pIt);
2127 PVDSTORAGE pStorage = (PVDSTORAGE)RTMemAllocZ(sizeof(VDSTORAGE));
2128 if (!pStorage)
2129 rc = VERR_NO_MEMORY;
2130
2131 pStorage->pFile = pIt;
2132 pStorage->pfnComplete = pfnCompleted;
2133 *ppStorage = pStorage;
2134 }
2135
2136 return rc;
2137}
2138
2139static DECLCALLBACK(int) tstVDIoFileClose(void *pvUser, void *pStorage)
2140{
2141 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2142
2143 RTMemFree(pIoStorage);
2144 return VINF_SUCCESS;
2145}
2146
2147static DECLCALLBACK(int) tstVDIoFileDelete(void *pvUser, const char *pcszFilename)
2148{
2149 int rc = VINF_SUCCESS;
2150 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2151 bool fFound = false;
2152
2153 /*
2154 * Some backends use ./ for paths, strip it.
2155 * @todo: Implement proper directory support for the
2156 * memory filesystem.
2157 */
2158 if ( strlen(pcszFilename) >= 2
2159 && *pcszFilename == '.'
2160 && pcszFilename[1] == '/')
2161 pcszFilename += 2;
2162
2163 /* Check if the file exists. */
2164 PVDFILE pIt = NULL;
2165 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
2166 {
2167 if (!RTStrCmp(pIt->pszName, pcszFilename))
2168 {
2169 fFound = true;
2170 break;
2171 }
2172 }
2173
2174 if (fFound)
2175 {
2176 RTListNodeRemove(&pIt->Node);
2177 VDIoBackendStorageDestroy(pIt->pIoStorage);
2178 RTStrFree(pIt->pszName);
2179 RTMemFree(pIt);
2180 }
2181 else
2182 rc = VERR_FILE_NOT_FOUND;
2183
2184 return rc;
2185}
2186
2187static DECLCALLBACK(int) tstVDIoFileMove(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
2188{
2189 int rc = VINF_SUCCESS;
2190 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2191 bool fFound = false;
2192
2193 /* Check if the file exists. */
2194 PVDFILE pIt = NULL;
2195 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
2196 {
2197 if (!RTStrCmp(pIt->pszName, pcszSrc))
2198 {
2199 fFound = true;
2200 break;
2201 }
2202 }
2203
2204 if (fFound)
2205 {
2206 char *pszNew = RTStrDup(pcszDst);
2207 if (pszNew)
2208 {
2209 RTStrFree(pIt->pszName);
2210 pIt->pszName = pszNew;
2211 }
2212 else
2213 rc = VERR_NO_MEMORY;
2214 }
2215 else
2216 rc = VERR_FILE_NOT_FOUND;
2217
2218 return rc;
2219}
2220
2221static DECLCALLBACK(int) tstVDIoFileGetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
2222{
2223 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
2224
2225 *pcbFreeSpace = ~0ULL; /** @todo: Implement */
2226 return VINF_SUCCESS;
2227}
2228
2229static DECLCALLBACK(int) tstVDIoFileGetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
2230{
2231 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
2232
2233 /** @todo: Implement */
2234 return VINF_SUCCESS;
2235}
2236
2237static DECLCALLBACK(int) tstVDIoFileGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
2238{
2239 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2240
2241 return VDIoBackendStorageGetSize(pIoStorage->pFile->pIoStorage, pcbSize);
2242}
2243
2244static DECLCALLBACK(int) tstVDIoFileSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
2245{
2246 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2247
2248 return VDIoBackendStorageSetSize(pIoStorage->pFile->pIoStorage, cbSize);
2249}
2250
2251static DECLCALLBACK(int) tstVDIoFileWriteSync(void *pvUser, void *pStorage, uint64_t uOffset,
2252 const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten)
2253{
2254 int rc = VINF_SUCCESS;
2255 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2256
2257 RTSGBUF SgBuf;
2258 RTSGSEG Seg;
2259
2260 Seg.pvSeg = (void *)pvBuffer;
2261 Seg.cbSeg = cbBuffer;
2262 RTSgBufInit(&SgBuf, &Seg, 1);
2263 rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_WRITE, uOffset,
2264 cbBuffer, &SgBuf, NULL, true /* fSync */);
2265 if (RT_SUCCESS(rc))
2266 {
2267 pIoStorage->pFile->cWrites++;
2268 if (pcbWritten)
2269 *pcbWritten = cbBuffer;
2270 }
2271
2272 return rc;
2273}
2274
2275static DECLCALLBACK(int) tstVDIoFileReadSync(void *pvUser, void *pStorage, uint64_t uOffset,
2276 void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
2277{
2278 int rc = VINF_SUCCESS;
2279 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2280
2281 RTSGBUF SgBuf;
2282 RTSGSEG Seg;
2283
2284 Seg.pvSeg = pvBuffer;
2285 Seg.cbSeg = cbBuffer;
2286 RTSgBufInit(&SgBuf, &Seg, 1);
2287 rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_READ, uOffset,
2288 cbBuffer, &SgBuf, NULL, true /* fSync */);
2289 if (RT_SUCCESS(rc))
2290 {
2291 pIoStorage->pFile->cReads++;
2292 if (pcbRead)
2293 *pcbRead = cbBuffer;
2294 }
2295
2296 return rc;
2297}
2298
2299static DECLCALLBACK(int) tstVDIoFileFlushSync(void *pvUser, void *pStorage)
2300{
2301 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2302 int rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_FLUSH, 0,
2303 0, NULL, NULL, true /* fSync */);
2304 pIoStorage->pFile->cFlushes++;
2305 return rc;
2306}
2307
2308static DECLCALLBACK(int) tstVDIoFileReadAsync(void *pvUser, void *pStorage, uint64_t uOffset,
2309 PCRTSGSEG paSegments, size_t cSegments,
2310 size_t cbRead, void *pvCompletion,
2311 void **ppTask)
2312{
2313 int rc = VINF_SUCCESS;
2314 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2315 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2316 RTSGBUF SgBuf;
2317
2318 RTSgBufInit(&SgBuf, paSegments, cSegments);
2319 rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_READ, uOffset,
2320 cbRead, &SgBuf, pvCompletion, false /* fSync */);
2321 if (RT_SUCCESS(rc))
2322 {
2323 pIoStorage->pFile->cAsyncReads++;
2324 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2325 }
2326
2327 return rc;
2328}
2329
2330static DECLCALLBACK(int) tstVDIoFileWriteAsync(void *pvUser, void *pStorage, uint64_t uOffset,
2331 PCRTSGSEG paSegments, size_t cSegments,
2332 size_t cbWrite, void *pvCompletion,
2333 void **ppTask)
2334{
2335 int rc = VINF_SUCCESS;
2336 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2337 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2338 RTSGBUF SgBuf;
2339
2340 RTSgBufInit(&SgBuf, paSegments, cSegments);
2341 rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_WRITE, uOffset,
2342 cbWrite, &SgBuf, pvCompletion, false /* fSync */);
2343 if (RT_SUCCESS(rc))
2344 {
2345 pIoStorage->pFile->cAsyncWrites++;
2346 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2347 }
2348
2349 return rc;
2350}
2351
2352static DECLCALLBACK(int) tstVDIoFileFlushAsync(void *pvUser, void *pStorage, void *pvCompletion,
2353 void **ppTask)
2354{
2355 int rc = VINF_SUCCESS;
2356 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
2357 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
2358
2359 rc = VDIoBackendTransfer(pIoStorage->pFile->pIoStorage, VDIOTXDIR_FLUSH, 0,
2360 0, NULL, pvCompletion, false /* fSync */);
2361 if (RT_SUCCESS(rc))
2362 {
2363 pIoStorage->pFile->cAsyncFlushes++;
2364 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
2365 }
2366
2367 return rc;
2368}
2369
2370static int tstVDIoTestInit(PVDIOTEST pIoTest, PVDTESTGLOB pGlob, bool fRandomAcc, uint64_t cbIo,
2371 size_t cbBlkSize, uint64_t offStart, uint64_t offEnd,
2372 unsigned uWriteChance, PVDPATTERN pPattern)
2373{
2374 int rc = VINF_SUCCESS;
2375
2376 RT_ZERO(*pIoTest);
2377 pIoTest->fRandomAccess = fRandomAcc;
2378 pIoTest->cbIo = cbIo;
2379 pIoTest->cbBlkIo = cbBlkSize;
2380 pIoTest->offStart = offStart;
2381 pIoTest->offEnd = offEnd;
2382 pIoTest->uWriteChance = uWriteChance;
2383 pIoTest->pIoRnd = pGlob->pIoRnd;
2384 pIoTest->pPattern = pPattern;
2385
2386 if (fRandomAcc)
2387 {
2388 uint64_t cbRange = pIoTest->offEnd < pIoTest->offStart
2389 ? pIoTest->offStart - pIoTest->offEnd
2390 : pIoTest->offEnd - pIoTest->offStart;
2391
2392 pIoTest->u.Rnd.cBlocks = cbRange / cbBlkSize + ((cbRange % cbBlkSize) ? 1 : 0);
2393 pIoTest->u.Rnd.cBlocksLeft = pIoTest->u.Rnd.cBlocks;
2394 pIoTest->u.Rnd.pbMapAccessed = (uint8_t *)RTMemAllocZ(pIoTest->u.Rnd.cBlocks / 8
2395 + ((pIoTest->u.Rnd.cBlocks % 8)
2396 ? 1
2397 : 0));
2398 if (!pIoTest->u.Rnd.pbMapAccessed)
2399 rc = VERR_NO_MEMORY;
2400 }
2401 else
2402 pIoTest->u.offNext = pIoTest->offEnd < pIoTest->offStart ? pIoTest->offStart - cbBlkSize : offStart;
2403
2404 return rc;
2405}
2406
2407static void tstVDIoTestDestroy(PVDIOTEST pIoTest)
2408{
2409 if (pIoTest->fRandomAccess)
2410 RTMemFree(pIoTest->u.Rnd.pbMapAccessed);
2411}
2412
2413static bool tstVDIoTestRunning(PVDIOTEST pIoTest)
2414{
2415 return pIoTest->cbIo > 0;
2416}
2417
2418static bool tstVDIoTestReqOutstanding(PVDIOREQ pIoReq)
2419{
2420 return pIoReq->fOutstanding;
2421}
2422
2423/**
2424 * Returns true with the given chance in percent.
2425 *
2426 * @returns true or false
2427 * @param iPercentage The percentage of the chance to return true.
2428 */
2429static bool tstVDIoTestIsTrue(PVDIOTEST pIoTest, int iPercentage)
2430{
2431 int uRnd = VDIoRndGetU32Ex(pIoTest->pIoRnd, 0, 100);
2432
2433 return (uRnd < iPercentage); /* This should be enough for our purpose */
2434}
2435
2436static int tstVDIoTestReqInit(PVDIOTEST pIoTest, PVDIOREQ pIoReq, void *pvUser)
2437{
2438 int rc = VINF_SUCCESS;
2439
2440 if (pIoTest->cbIo)
2441 {
2442 /* Read or Write? */
2443 pIoReq->enmTxDir = tstVDIoTestIsTrue(pIoTest, pIoTest->uWriteChance) ? VDIOREQTXDIR_WRITE : VDIOREQTXDIR_READ;
2444 pIoReq->cbReq = RT_MIN(pIoTest->cbBlkIo, pIoTest->cbIo);
2445 pIoTest->cbIo -= pIoReq->cbReq;
2446 pIoReq->DataSeg.cbSeg = pIoReq->cbReq;
2447
2448 if (pIoReq->enmTxDir == VDIOREQTXDIR_WRITE)
2449 {
2450 if (pIoTest->pPattern)
2451 rc = tstVDIoPatternGetBuffer(pIoTest->pPattern, &pIoReq->DataSeg.pvSeg, pIoReq->cbReq);
2452 else
2453 rc = VDIoRndGetBuffer(pIoTest->pIoRnd, &pIoReq->DataSeg.pvSeg, pIoReq->cbReq);
2454 AssertRC(rc);
2455 }
2456 else
2457 {
2458 /* Read */
2459 pIoReq->DataSeg.pvSeg = pIoReq->pvBufRead;
2460 }
2461
2462 if (RT_SUCCESS(rc))
2463 {
2464 RTSgBufInit(&pIoReq->SgBuf, &pIoReq->DataSeg, 1);
2465
2466 if (pIoTest->fRandomAccess)
2467 {
2468 int idx = -1;
2469
2470 idx = ASMBitFirstClear(pIoTest->u.Rnd.pbMapAccessed, pIoTest->u.Rnd.cBlocks);
2471
2472 /* In case this is the last request we don't need to search further. */
2473 if (pIoTest->u.Rnd.cBlocksLeft > 1)
2474 {
2475 int idxIo;
2476 idxIo = VDIoRndGetU32Ex(pIoTest->pIoRnd, idx, pIoTest->u.Rnd.cBlocks - 1);
2477
2478 /*
2479 * If the bit is marked free use it, otherwise search for the next free bit
2480 * and if that doesn't work use the first free bit.
2481 */
2482 if (ASMBitTest(pIoTest->u.Rnd.pbMapAccessed, idxIo))
2483 {
2484 idxIo = ASMBitNextClear(pIoTest->u.Rnd.pbMapAccessed, pIoTest->u.Rnd.cBlocks, idxIo);
2485 if (idxIo != -1)
2486 idx = idxIo;
2487 }
2488 else
2489 idx = idxIo;
2490 }
2491
2492 Assert(idx != -1);
2493 pIoReq->off = (uint64_t)idx * pIoTest->cbBlkIo;
2494 pIoTest->u.Rnd.cBlocksLeft--;
2495 if (!pIoTest->u.Rnd.cBlocksLeft)
2496 {
2497 /* New round, clear everything. */
2498 ASMBitClearRange(pIoTest->u.Rnd.pbMapAccessed, 0, pIoTest->u.Rnd.cBlocks);
2499 pIoTest->u.Rnd.cBlocksLeft = pIoTest->u.Rnd.cBlocks;
2500 }
2501 else
2502 ASMBitSet(pIoTest->u.Rnd.pbMapAccessed, idx);
2503 }
2504 else
2505 {
2506 pIoReq->off = pIoTest->u.offNext;
2507 if (pIoTest->offEnd < pIoTest->offStart)
2508 {
2509 pIoTest->u.offNext = pIoTest->u.offNext == 0
2510 ? pIoTest->offEnd - pIoTest->cbBlkIo
2511 : RT_MAX(pIoTest->offEnd, pIoTest->u.offNext - pIoTest->cbBlkIo);
2512 }
2513 else
2514 {
2515 pIoTest->u.offNext = pIoTest->u.offNext + pIoTest->cbBlkIo >= pIoTest->offEnd
2516 ? 0
2517 : RT_MIN(pIoTest->offEnd, pIoTest->u.offNext + pIoTest->cbBlkIo);
2518 }
2519 }
2520 pIoReq->pvUser = pvUser;
2521 pIoReq->fOutstanding = true;
2522 }
2523 }
2524 else
2525 rc = VERR_ACCESS_DENIED;
2526
2527 return rc;
2528}
2529
2530static void tstVDIoTestReqComplete(void *pvUser1, void *pvUser2, int rcReq)
2531{
2532 PVDIOREQ pIoReq = (PVDIOREQ)pvUser1;
2533 RTSEMEVENT hEventSem = (RTSEMEVENT)pvUser2;
2534 PVDDISK pDisk = (PVDDISK)pIoReq->pvUser;
2535
2536 LogFlow(("Request %d completed\n", pIoReq->idx));
2537
2538 if (pDisk->pMemDiskVerify)
2539 {
2540 switch (pIoReq->enmTxDir)
2541 {
2542 case VDIOREQTXDIR_READ:
2543 {
2544 RTCritSectEnter(&pDisk->CritSectVerify);
2545 RTSgBufReset(&pIoReq->SgBuf);
2546
2547 if (VDMemDiskCmp(pDisk->pMemDiskVerify, pIoReq->off, pIoReq->cbReq,
2548 &pIoReq->SgBuf))
2549 RTTestFailed(pDisk->pTestGlob->hTest, "Corrupted disk at offset %llu!\n", pIoReq->off);
2550 RTCritSectLeave(&pDisk->CritSectVerify);
2551 }
2552 case VDIOREQTXDIR_WRITE:
2553 {
2554 RTCritSectEnter(&pDisk->CritSectVerify);
2555 RTSgBufReset(&pIoReq->SgBuf);
2556
2557 int rc = VDMemDiskWrite(pDisk->pMemDiskVerify, pIoReq->off, pIoReq->cbReq,
2558 &pIoReq->SgBuf);
2559 AssertRC(rc);
2560 RTCritSectLeave(&pDisk->CritSectVerify);
2561 break;
2562 }
2563 case VDIOREQTXDIR_FLUSH:
2564 case VDIOREQTXDIR_DISCARD:
2565 break;
2566 }
2567 }
2568
2569 ASMAtomicXchgBool(&pIoReq->fOutstanding, false);
2570 RTSemEventSignal(hEventSem);
2571 return;
2572}
2573
2574/**
2575 * Returns the disk handle by name or NULL if not found
2576 *
2577 * @returns Disk handle or NULL if the disk could not be found.
2578 *
2579 * @param pGlob Global test state.
2580 * @param pcszDisk Name of the disk to get.
2581 */
2582static PVDDISK tstVDIoGetDiskByName(PVDTESTGLOB pGlob, const char *pcszDisk)
2583{
2584 PVDDISK pIt = NULL;
2585 bool fFound = false;
2586
2587 LogFlowFunc(("pGlob=%#p pcszDisk=%s\n", pGlob, pcszDisk));
2588
2589 RTListForEach(&pGlob->ListDisks, pIt, VDDISK, ListNode)
2590 {
2591 if (!RTStrCmp(pIt->pszName, pcszDisk))
2592 {
2593 fFound = true;
2594 break;
2595 }
2596 }
2597
2598 LogFlowFunc(("return %#p\n", fFound ? pIt : NULL));
2599 return fFound ? pIt : NULL;
2600}
2601
2602/**
2603 * Returns the I/O pattern handle by name of NULL if not found.
2604 *
2605 * @returns I/O pattern handle or NULL if the pattern could not be found.
2606 *
2607 * @param pGlob Global test state.
2608 * @param pcszName Name of the pattern.
2609 */
2610static PVDPATTERN tstVDIoGetPatternByName(PVDTESTGLOB pGlob, const char *pcszName)
2611{
2612 PVDPATTERN pIt = NULL;
2613 bool fFound = false;
2614
2615 LogFlowFunc(("pGlob=%#p pcszName=%s\n", pGlob, pcszName));
2616
2617 RTListForEach(&pGlob->ListPatterns, pIt, VDPATTERN, ListNode)
2618 {
2619 if (!RTStrCmp(pIt->pszName, pcszName))
2620 {
2621 fFound = true;
2622 break;
2623 }
2624 }
2625
2626 LogFlowFunc(("return %#p\n", fFound ? pIt : NULL));
2627 return fFound ? pIt : NULL;
2628}
2629
2630/**
2631 * Creates a new pattern with the given name and an
2632 * allocated pattern buffer.
2633 *
2634 * @returns Pointer to a new pattern buffer or NULL on failure.
2635 * @param pcszName Name of the pattern.
2636 * @param cbPattern Size of the pattern buffer.
2637 */
2638static PVDPATTERN tstVDIoPatternCreate(const char *pcszName, size_t cbPattern)
2639{
2640 PVDPATTERN pPattern = (PVDPATTERN)RTMemAllocZ(sizeof(VDPATTERN));
2641 char *pszName = RTStrDup(pcszName);
2642 void *pvPattern = RTMemAllocZ(cbPattern);
2643
2644 if (pPattern && pszName && pvPattern)
2645 {
2646 pPattern->pszName = pszName;
2647 pPattern->pvPattern = pvPattern;
2648 pPattern->cbPattern = cbPattern;
2649 }
2650 else
2651 {
2652 if (pPattern)
2653 RTMemFree(pPattern);
2654 if (pszName)
2655 RTStrFree(pszName);
2656 if (pvPattern)
2657 RTMemFree(pvPattern);
2658
2659 pPattern = NULL;
2660 pszName = NULL;
2661 pvPattern = NULL;
2662 }
2663
2664 return pPattern;
2665}
2666
2667static int tstVDIoPatternGetBuffer(PVDPATTERN pPattern, void **ppv, size_t cb)
2668{
2669 AssertPtrReturn(pPattern, VERR_INVALID_POINTER);
2670 AssertPtrReturn(ppv, VERR_INVALID_POINTER);
2671 AssertReturn(cb > 0, VERR_INVALID_PARAMETER);
2672
2673 if (cb > pPattern->cbPattern)
2674 return VERR_INVALID_PARAMETER;
2675
2676 *ppv = pPattern->pvPattern;
2677 return VINF_SUCCESS;
2678}
2679
2680/**
2681 * Executes the given script.
2682 *
2683 * @returns nothing.
2684 * @param pszScript The script to execute.
2685 */
2686static void tstVDIoScriptExec(const char *pszScript)
2687{
2688 int rc = VINF_SUCCESS;
2689 VDTESTGLOB GlobTest; /**< Global test data. */
2690
2691 memset(&GlobTest, 0, sizeof(VDTESTGLOB));
2692 RTListInit(&GlobTest.ListFiles);
2693 RTListInit(&GlobTest.ListDisks);
2694 RTListInit(&GlobTest.ListPatterns);
2695 GlobTest.pszIoBackend = RTStrDup("memory");
2696 if (!GlobTest.pszIoBackend)
2697 {
2698 RTPrintf("Out of memory allocating I/O backend string\n");
2699 return;
2700 }
2701
2702 /* Init global test data. */
2703 GlobTest.VDIfError.pfnError = tstVDError;
2704 GlobTest.VDIfError.pfnMessage = tstVDMessage;
2705
2706 rc = VDInterfaceAdd(&GlobTest.VDIfError.Core, "tstVDIo_VDIError", VDINTERFACETYPE_ERROR,
2707 NULL, sizeof(VDINTERFACEERROR), &GlobTest.pInterfacesDisk);
2708 AssertRC(rc);
2709
2710 GlobTest.VDIfIo.pfnOpen = tstVDIoFileOpen;
2711 GlobTest.VDIfIo.pfnClose = tstVDIoFileClose;
2712 GlobTest.VDIfIo.pfnDelete = tstVDIoFileDelete;
2713 GlobTest.VDIfIo.pfnMove = tstVDIoFileMove;
2714 GlobTest.VDIfIo.pfnGetFreeSpace = tstVDIoFileGetFreeSpace;
2715 GlobTest.VDIfIo.pfnGetModificationTime = tstVDIoFileGetModificationTime;
2716 GlobTest.VDIfIo.pfnGetSize = tstVDIoFileGetSize;
2717 GlobTest.VDIfIo.pfnSetSize = tstVDIoFileSetSize;
2718 GlobTest.VDIfIo.pfnWriteSync = tstVDIoFileWriteSync;
2719 GlobTest.VDIfIo.pfnReadSync = tstVDIoFileReadSync;
2720 GlobTest.VDIfIo.pfnFlushSync = tstVDIoFileFlushSync;
2721 GlobTest.VDIfIo.pfnReadAsync = tstVDIoFileReadAsync;
2722 GlobTest.VDIfIo.pfnWriteAsync = tstVDIoFileWriteAsync;
2723 GlobTest.VDIfIo.pfnFlushAsync = tstVDIoFileFlushAsync;
2724
2725 rc = VDInterfaceAdd(&GlobTest.VDIfIo.Core, "tstVDIo_VDIIo", VDINTERFACETYPE_IO,
2726 &GlobTest, sizeof(VDINTERFACEIO), &GlobTest.pInterfacesImages);
2727 AssertRC(rc);
2728
2729 rc = RTTestCreate("tstVDIo", &GlobTest.hTest);
2730 if (RT_SUCCESS(rc))
2731 {
2732 /* Init I/O backend. */
2733 rc = VDIoBackendCreate(&GlobTest.pIoBackend);
2734 if (RT_SUCCESS(rc))
2735 {
2736 VDSCRIPTCTX hScriptCtx = NULL;
2737 rc = VDScriptCtxCreate(&hScriptCtx);
2738 if (RT_SUCCESS(rc))
2739 {
2740 RTTEST_CHECK_RC_OK(GlobTest.hTest,
2741 VDScriptCtxCallbacksRegister(hScriptCtx, g_aScriptActions, g_cScriptActions, &GlobTest));
2742
2743 RTTestBanner(GlobTest.hTest);
2744 rc = VDScriptCtxLoadScript(hScriptCtx, pszScript);
2745 if (RT_FAILURE(rc))
2746 {
2747 RTPrintf("Loading the script failed rc=%Rrc\n", rc);
2748 }
2749 else
2750 rc = VDScriptCtxCallFn(hScriptCtx, "main", NULL, 0);
2751 VDScriptCtxDestroy(hScriptCtx);
2752 }
2753 VDIoBackendDestroy(GlobTest.pIoBackend);
2754 }
2755 else
2756 RTPrintf("Creating the I/O backend failed rc=%Rrc\n");
2757
2758 RTTestSummaryAndDestroy(GlobTest.hTest);
2759 }
2760 else
2761 RTStrmPrintf(g_pStdErr, "tstVDIo: fatal error: RTTestCreate failed with rc=%Rrc\n", rc);
2762
2763 RTStrFree(GlobTest.pszIoBackend);
2764}
2765
2766/**
2767 * Executes the given I/O script using the new scripting engine.
2768 *
2769 * @returns nothing.
2770 *
2771 * @param pcszFilename The script to execute.
2772 */
2773static void tstVDIoScriptRun(const char *pcszFilename)
2774{
2775 int rc = VINF_SUCCESS;
2776 void *pvFile = NULL;
2777 size_t cbFile = 0;
2778
2779 rc = RTFileReadAll(pcszFilename, &pvFile, &cbFile);
2780 if (RT_SUCCESS(rc))
2781 {
2782 char *pszScript = RTStrDupN((char *)pvFile, cbFile);
2783 RTFileReadAllFree(pvFile, cbFile);
2784
2785 AssertPtr(pszScript);
2786 tstVDIoScriptExec(pszScript);
2787 RTStrFree(pszScript);
2788 }
2789 else
2790 RTPrintf("Opening the script failed: %Rrc\n", rc);
2791
2792}
2793
2794/**
2795 * Run builtin tests.
2796 *
2797 * @returns nothing.
2798 */
2799static void tstVDIoRunBuiltinTests(void)
2800{
2801 /*
2802 * We need quite a bit of RAM for the builtin tests. Skip it if there
2803 * is not enough free RAM available.
2804 */
2805 uint64_t cbFree = 0;
2806 int rc = RTSystemQueryAvailableRam(&cbFree);
2807 if ( RT_FAILURE(rc)
2808 || cbFree < (UINT64_C(6) * _1G))
2809 {
2810 RTStrmPrintf(g_pStdErr, "tstVDIo: fatal error: Failed to query available RAM or not enough available, skipping (rc=%Rrc cbFree=%llu)\n",
2811 rc, cbFree);
2812 return;
2813 }
2814
2815 for (unsigned i = 0; i < g_cVDIoTests; i++)
2816 {
2817 char *pszScript = RTStrDupN((const char *)g_aVDIoTests[i].pch, g_aVDIoTests[i].cb);
2818
2819 AssertPtr(pszScript);
2820 tstVDIoScriptExec(pszScript);
2821 }
2822}
2823
2824/**
2825 * Shows help message.
2826 */
2827static void printUsage(void)
2828{
2829 RTPrintf("Usage:\n"
2830 "--script <filename> Script to execute\n");
2831}
2832
2833static const RTGETOPTDEF g_aOptions[] =
2834{
2835 { "--script", 's', RTGETOPT_REQ_STRING },
2836 { "--help", 'h', RTGETOPT_REQ_NOTHING }
2837};
2838
2839int main(int argc, char *argv[])
2840{
2841 RTR3InitExe(argc, &argv, 0);
2842 int rc;
2843 RTGETOPTUNION ValueUnion;
2844 RTGETOPTSTATE GetState;
2845 char c;
2846
2847 rc = VDInit();
2848 if (RT_FAILURE(rc))
2849 return RTEXITCODE_FAILURE;
2850
2851 if (argc == 1)
2852 {
2853 tstVDIoRunBuiltinTests();
2854 return RTEXITCODE_SUCCESS;
2855 }
2856
2857 RTGetOptInit(&GetState, argc, argv, g_aOptions,
2858 RT_ELEMENTS(g_aOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
2859
2860 while ( RT_SUCCESS(rc)
2861 && (c = RTGetOpt(&GetState, &ValueUnion)))
2862 {
2863 switch (c)
2864 {
2865 case 's':
2866 tstVDIoScriptRun(ValueUnion.psz);
2867 break;
2868 case 'h':
2869 printUsage();
2870 break;
2871 default: /* Default is to run built in tests if no arguments are given (automated testing). */
2872 tstVDIoRunBuiltinTests();
2873 }
2874 }
2875
2876 rc = VDShutdown();
2877 if (RT_FAILURE(rc))
2878 RTPrintf("tstVDIo: unloading backends failed! rc=%Rrc\n", rc);
2879
2880 return RTEXITCODE_SUCCESS;
2881}
2882
Note: See TracBrowser for help on using the repository browser.

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