VirtualBox

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

Last change on this file since 57671 was 57533, checked in by vboxsync, 9 years ago

tstVDIo: Fix for the out of memory case

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

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