VirtualBox

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

Last change on this file since 56529 was 56529, checked in by vboxsync, 10 years ago

tstVDIo: Change the implementation to calculate the throughput in KB/s. Before it would return ~(uint64_t)0 on at least one testbox causing Out of Range errors with the SQL database when inserting that value and returning stale data in the web interface. The current approach while still not very sophisticated should avoid that and the error margin introduced is far below the normal fluctuations during the tests

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