VirtualBox

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

Last change on this file since 103435 was 103005, checked in by vboxsync, 11 months ago

iprt/asm.h,*: Split out the ASMMem* and related stuff into a separate header, asm-mem.h, so that we can get the RT_ASM_PAGE_SIZE stuff out of the way.

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