VirtualBox

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

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

tstVDIo: Implement dumptofile action to dump memory backed files to disk

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