VirtualBox

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

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

Storage/tstVDIo: Plug leaks, modifications and start turning it into a proper testcase for unit tests

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