VirtualBox

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

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

Storage/tstVDIo: Avoid division by 0

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