VirtualBox

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

Last change on this file since 35579 was 35579, checked in by vboxsync, 14 years ago

scm: cleanups and adjustments

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 41.7 KB
Line 
1/** @file
2 *
3 * VBox HDD container test utility - I/O replay.
4 */
5
6/*
7 * Copyright (C) 2011 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17#define LOGGROUP LOGGROUP_DEFAULT
18#include <VBox/vd.h>
19#include <VBox/err.h>
20#include <VBox/log.h>
21#include <iprt/asm.h>
22#include <iprt/string.h>
23#include <iprt/stream.h>
24#include <iprt/mem.h>
25#include <iprt/initterm.h>
26#include <iprt/getopt.h>
27#include <iprt/list.h>
28#include <iprt/ctype.h>
29
30#include "VDMemDisk.h"
31#include "VDIoBackendMem.h"
32
33/**
34 * A virtual file backed by memory.
35 */
36typedef struct VDFILE
37{
38 /** Pointer to the next file. */
39 RTLISTNODE Node;
40 /** Name of the file. */
41 char *pszName;
42 /** Memory file baking the file. */
43 PVDMEMDISK pMemDisk;
44 /** Completion callback of the VD layer. */
45 PFNVDCOMPLETED pfnComplete;
46} VDFILE, *PVDFILE;
47
48/**
49 * Global VD test state.
50 */
51typedef struct VDTESTGLOB
52{
53 /** HDD handle to operate on. */
54 PVBOXHDD pVD;
55 /** Head of the active file list. */
56 RTLISTNODE ListFiles;
57 /** Memory I/O backend. */
58 PVDIOBACKENDMEM pIoBackend;
59 /** Error interface. */
60 VDINTERFACE VDIError;
61 /** Error interface callbacks. */
62 VDINTERFACEERROR VDIErrorCallbacks;
63 /** Pointer to the per disk interface list. */
64 PVDINTERFACE pInterfacesDisk;
65 /** I/O interface. */
66 VDINTERFACE VDIIo;
67 /** I/O interface callbacks. */
68 VDINTERFACEIO VDIIoCallbacks;
69 /** Pointer to the per image interface list. */
70 PVDINTERFACE pInterfacesImages;
71 /** Physical CHS Geometry. */
72 VDGEOMETRY PhysGeom;
73 /** Logical CHS geometry. */
74 VDGEOMETRY LogicalGeom;
75} VDTESTGLOB, *PVDTESTGLOB;
76
77/**
78 * Argument types.
79 */
80typedef enum VDSCRIPTARGTYPE
81{
82 /** Argument is a string. */
83 VDSCRIPTARGTYPE_STRING = 0,
84 /** Argument is a 64bit unsigned number. */
85 VDSCRIPTARGTYPE_UNSIGNED_NUMBER,
86 /** Argument is a 64bit signed number. */
87 VDSCRIPTARGTYPE_SIGNED_NUMBER,
88 /** Arugment is a unsigned 64bit range */
89 VDSCRIPTARGTYPE_UNSIGNED_RANGE,
90 /** Arugment is a boolean. */
91 VDSCRIPTARGTYPE_BOOL
92} VDSCRIPTARGTYPE;
93
94/**
95 * Script argument.
96 */
97typedef struct VDSCRIPTARG
98{
99 /** Argument identifier. */
100 char chId;
101 /** Type of the argument. */
102 VDSCRIPTARGTYPE enmType;
103 /** Type depndent data. */
104 union
105 {
106 /** String. */
107 const char *pcszString;
108 /** Bool. */
109 bool fFlag;
110 /** unsigned number. */
111 uint64_t u64;
112 /** Signed number. */
113 int64_t i64;
114 /** Unsigned range. */
115 struct
116 {
117 uint64_t Start;
118 uint64_t End;
119 } Range;
120 } u;
121} VDSCRIPTARG, *PVDSCRIPTARG;
122
123/** Script action handler. */
124typedef DECLCALLBACK(int) FNVDSCRIPTACTION(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
125/** Pointer to a script action handler. */
126typedef FNVDSCRIPTACTION *PFNVDSCRIPTACTION;
127
128/**
129 * Script argument descriptor.
130 */
131typedef struct VDSCRIPTARGDESC
132{
133 /** Name of the arugment. */
134 const char *pcszName;
135 /** Identifier for the argument. */
136 char chId;
137 /** Type of the argument. */
138 VDSCRIPTARGTYPE enmType;
139 /** Flags */
140 uint32_t fFlags;
141} VDSCRIPTARGDESC, *PVDSCRIPTARGDESC;
142/** Pointer to a const script argument descriptor. */
143typedef const VDSCRIPTARGDESC *PCVDSCRIPTARGDESC;
144
145/** Flag whether the argument is mandatory. */
146#define VDSCRIPTARGDESC_FLAG_MANDATORY RT_BIT(0)
147/** Flag whether the number can have a size suffix (K|M|G) */
148#define VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX RT_BIT(1)
149
150/**
151 * Script action.
152 */
153typedef struct VDSCRIPTACTION
154{
155 /** Action name. */
156 const char *pcszAction;
157 /** Pointer to the arguments. */
158 const PCVDSCRIPTARGDESC paArgDesc;
159 /** Number of arugments in the array. */
160 unsigned cArgDescs;
161 /** Pointer to the action handler. */
162 PFNVDSCRIPTACTION pfnHandler;
163} VDSCRIPTACTION, *PVDSCRIPTACTION;
164
165typedef const VDSCRIPTACTION *PCVDSCRIPTACTION;
166
167static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
168static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
169static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
170static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
171static DECLCALLBACK(int) vdScriptHandlerMerge(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
172static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
173
174/* create action */
175const VDSCRIPTARGDESC g_aArgCreate[] =
176{
177 /* pcszName chId enmType fFlags */
178 {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
179 {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
180 {"backend", 'b', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
181 {"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
182};
183
184/* open action */
185const VDSCRIPTARGDESC g_aArgOpen[] =
186{
187 /* pcszName chId enmType fFlags */
188 {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
189 {"backend", 'b', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
190};
191
192/* write action */
193const VDSCRIPTARGDESC g_aArgIo[] =
194{
195 /* pcszName chId enmType fFlags */
196 {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0},
197 {"max-reqs", 'l', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0},
198 {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
199 {"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
200 {"blocksize", 'b', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
201 {"off", 'o', VDSCRIPTARGTYPE_UNSIGNED_RANGE, VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
202 {"reads", 'r', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY},
203 {"writes", 'w', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY}
204};
205
206/* flush action */
207const VDSCRIPTARGDESC g_aArgFlush[] =
208{
209 /* pcszName chId enmType fFlags */
210 {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0}
211};
212
213/* merge action */
214const VDSCRIPTARGDESC g_aArgMerge[] =
215{
216 /* pcszName chId enmType fFlags */
217 {"forward", 'f', VDSCRIPTARGTYPE_BOOL, VDSCRIPTARGDESC_FLAG_MANDATORY}
218};
219
220/* close action */
221const VDSCRIPTARGDESC g_aArgClose[] =
222{
223 /* pcszName chId enmType fFlags */
224 {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
225 {"delete", 'd', VDSCRIPTARGTYPE_BOOL , VDSCRIPTARGDESC_FLAG_MANDATORY}
226};
227
228const VDSCRIPTACTION g_aScriptActions[] =
229{
230 /* pcszAction paArgDesc cArgDescs pfnHandler */
231 {"create", g_aArgCreate, RT_ELEMENTS(g_aArgCreate), vdScriptHandlerCreate},
232 {"open", g_aArgOpen, RT_ELEMENTS(g_aArgOpen), vdScriptHandlerOpen},
233 {"io", g_aArgIo, RT_ELEMENTS(g_aArgIo), vdScriptHandlerIo},
234 {"flush", g_aArgFlush, RT_ELEMENTS(g_aArgFlush), vdScriptHandlerFlush},
235 {"close", g_aArgClose, RT_ELEMENTS(g_aArgClose), vdScriptHandlerClose},
236 {"merge", g_aArgMerge, RT_ELEMENTS(g_aArgMerge), vdScriptHandlerMerge},
237};
238
239const unsigned g_cScriptActions = RT_ELEMENTS(g_aScriptActions);
240
241static void tstVDError(void *pvUser, int rc, RT_SRC_POS_DECL,
242 const char *pszFormat, va_list va)
243{
244 RTPrintf("tstVD: Error %Rrc at %s:%u (%s): ", rc, RT_SRC_POS_ARGS);
245 RTPrintfV(pszFormat, va);
246 RTPrintf("\n");
247}
248
249static int tstVDMessage(void *pvUser, const char *pszFormat, va_list va)
250{
251 RTPrintf("tstVD: ");
252 RTPrintfV(pszFormat, va);
253 return VINF_SUCCESS;
254}
255
256static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
257{
258 int rc = VINF_SUCCESS;
259 uint64_t cbSize = 0;
260 const char *pcszBackend = NULL;
261 const char *pcszImage = NULL;
262 bool fBase = false;
263
264 for (unsigned i = 0; i < cScriptArgs; i++)
265 {
266 switch (paScriptArgs[i].chId)
267 {
268 case 'm':
269 {
270 if (!RTStrICmp(paScriptArgs[i].u.pcszString, "base"))
271 fBase = true;
272 else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "diff"))
273 fBase = false;
274 else
275 {
276 RTPrintf("Invalid image mode '%s' given\n", paScriptArgs[i].u.pcszString);
277 rc = VERR_INVALID_PARAMETER;
278 }
279 break;
280 }
281 case 'n':
282 {
283 pcszImage = paScriptArgs[i].u.pcszString;
284 break;
285 }
286 case 'b':
287 {
288 pcszBackend = paScriptArgs[i].u.pcszString;
289 break;
290 }
291 case 's':
292 {
293 cbSize = paScriptArgs[i].u.u64;
294 break;
295 }
296 default:
297 AssertMsgFailed(("Invalid argument given!\n"));
298 }
299
300 if (RT_FAILURE(rc))
301 break;
302 }
303
304 if (RT_SUCCESS(rc))
305 {
306 if (fBase)
307 rc = VDCreateBase(pGlob->pVD, pcszBackend, pcszImage, cbSize, 0, NULL,
308 &pGlob->PhysGeom, &pGlob->LogicalGeom,
309 NULL, VD_OPEN_FLAGS_ASYNC_IO, pGlob->pInterfacesImages, NULL);
310 else
311 rc = VDCreateDiff(pGlob->pVD, pcszBackend, pcszImage, 0, NULL, NULL, NULL, VD_OPEN_FLAGS_ASYNC_IO,
312 pGlob->pInterfacesImages, NULL);
313 }
314
315 return rc;
316}
317
318static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
319{
320 int rc = VINF_SUCCESS;
321 const char *pcszBackend = NULL;
322 const char *pcszImage = NULL;
323
324 for (unsigned i = 0; i < cScriptArgs; i++)
325 {
326 switch (paScriptArgs[i].chId)
327 {
328 case 'n':
329 {
330 pcszImage = paScriptArgs[i].u.pcszString;
331 break;
332 }
333 case 'b':
334 {
335 pcszBackend = paScriptArgs[i].u.pcszString;
336 break;
337 }
338 default:
339 AssertMsgFailed(("Invalid argument given!\n"));
340 }
341
342 if (RT_FAILURE(rc))
343 break;
344 }
345
346 if (RT_SUCCESS(rc))
347 {
348 rc = VDOpen(pGlob->pVD, pcszBackend, pcszImage, VD_OPEN_FLAGS_ASYNC_IO, pGlob->pInterfacesImages);
349 }
350
351 return rc;
352}
353
354static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
355{
356 int rc = VINF_SUCCESS;
357 bool fAsync = false;
358 bool fRandomAcc = false;
359 uint64_t cbIo = 0;
360 uint64_t cbBlkSize = 0;
361 bool fDataProviderRnd = false;
362 bool fPrintStats = false;
363 uint64_t offStart = 0;
364 uint64_t offEnd = 0;
365 unsigned cMaxReqs = 0;
366 uint8_t uWriteChance = 0;
367 uint8_t uReadChance = 0;
368
369 offEnd = VDGetSize(pGlob->pVD, VD_LAST_IMAGE);
370 if (offEnd == 0)
371 return VERR_INVALID_STATE;
372 cbIo = offEnd;
373
374 for (unsigned i = 0; i < cScriptArgs; i++)
375 {
376 switch (paScriptArgs[i].chId)
377 {
378 case 'a':
379 {
380 fAsync = paScriptArgs[i].u.fFlag;
381 break;
382 }
383 case 'l':
384 {
385 cMaxReqs = paScriptArgs[i].u.u64;
386 break;
387 }
388 case 'm':
389 {
390 if (!RTStrICmp(paScriptArgs[i].u.pcszString, "seq"))
391 fRandomAcc = false;
392 else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "rnd"))
393 fRandomAcc = true;
394 else
395 {
396 RTPrintf("Invalid access mode '%s'\n", paScriptArgs[i].u.pcszString);
397 rc = VERR_INVALID_PARAMETER;
398 }
399 break;
400 }
401 case 's':
402 {
403 cbIo = paScriptArgs[i].u.u64;
404 break;
405 }
406 case 'b':
407 {
408 cbBlkSize = paScriptArgs[i].u.u64;
409 break;
410 }
411 case 'o':
412 {
413 offStart = paScriptArgs[i].u.Range.Start;
414 offEnd = paScriptArgs[i].u.Range.End;
415 break;
416 }
417 case 'r':
418 {
419 uReadChance = (uint8_t)paScriptArgs[i].u.u64;
420 break;
421 }
422 case 'w':
423 {
424 uWriteChance = (uint8_t)paScriptArgs[i].u.u64;
425 break;
426 }
427 default:
428 AssertMsgFailed(("Invalid argument given!\n"));
429 }
430
431 if (RT_FAILURE(rc))
432 break;
433 }
434
435 rc = VERR_NOT_IMPLEMENTED;
436 return rc;
437}
438
439static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
440{
441 int rc = VINF_SUCCESS;
442 bool fAsync = false;
443
444 if (cScriptArgs == 1 && paScriptArgs[0].chId == 'a')
445 fAsync = paScriptArgs[0].u.fFlag;
446
447 if (fAsync)
448 {
449 /** @todo */
450 rc = VERR_NOT_IMPLEMENTED;
451 }
452 else
453 rc = VDFlush(pGlob->pVD);
454
455 return rc;
456}
457
458static DECLCALLBACK(int) vdScriptHandlerMerge(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
459{
460 return VERR_NOT_IMPLEMENTED;
461}
462
463static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
464{
465 int rc = VINF_SUCCESS;
466 bool fAll = false;
467 bool fDelete = false;
468
469 for (unsigned i = 0; i < cScriptArgs; i++)
470 {
471 switch (paScriptArgs[i].chId)
472 {
473 case 'm':
474 {
475 if (!RTStrICmp(paScriptArgs[i].u.pcszString, "all"))
476 fAll = true;
477 else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "single"))
478 fAll = false;
479 else
480 {
481 RTPrintf("Invalid mode '%s' given\n", paScriptArgs[i].u.pcszString);
482 rc = VERR_INVALID_PARAMETER;
483 }
484 break;
485 }
486 case 'd':
487 {
488 fDelete = paScriptArgs[i].u.fFlag;
489 break;
490 }
491 default:
492 AssertMsgFailed(("Invalid argument given!\n"));
493 }
494
495 if (RT_FAILURE(rc))
496 break;
497 }
498
499 if ( RT_SUCCESS(rc)
500 && fAll
501 && fDelete)
502 {
503 RTPrintf("mode=all doesn't work with delete=yes\n");
504 rc = VERR_INVALID_PARAMETER;
505 }
506
507 if (RT_SUCCESS(rc))
508 {
509 if (fAll)
510 rc = VDCloseAll(pGlob->pVD);
511 else
512 rc = VDClose(pGlob->pVD, fDelete);
513 }
514 return rc;
515}
516
517static DECLCALLBACK(int) tstVDIoFileOpen(void *pvUser, const char *pszLocation,
518 uint32_t fOpen,
519 PFNVDCOMPLETED pfnCompleted,
520 void **ppStorage)
521{
522 int rc = VINF_SUCCESS;
523 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
524 bool fFound = false;
525
526 /* Check if the file exists. */
527 PVDFILE pIt = NULL;
528 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
529 {
530 if (!RTStrCmp(pIt->pszName, pszLocation))
531 {
532 fFound = true;
533 break;
534 }
535 }
536
537 if (fFound && pIt->pfnComplete)
538 rc = VERR_FILE_LOCK_FAILED;
539 else if (fOpen & RTFILE_O_CREATE)
540 {
541 /* If the file exists delete the memory disk. */
542 if (fFound)
543 rc = VDMemDiskSetSize(pIt->pMemDisk, 0);
544 else
545 {
546 /* Create completey new. */
547 pIt = (PVDFILE)RTMemAllocZ(sizeof(VDFILE));
548 if (pIt)
549 {
550 pIt->pfnComplete = pfnCompleted;
551 pIt->pszName = RTStrDup(pszLocation);
552
553 if (pIt->pszName)
554 {
555 rc = VDMemDiskCreate(&pIt->pMemDisk, 0);
556 }
557 else
558 rc = VERR_NO_MEMORY;
559
560 if (RT_FAILURE(rc))
561 {
562 if (pIt->pszName)
563 RTStrFree(pIt->pszName);
564 RTMemFree(pIt);
565 }
566 }
567 else
568 rc = VERR_NO_MEMORY;
569
570 RTListAppend(&pGlob->ListFiles, &pIt->Node);
571 }
572 }
573 else if (fOpen & RTFILE_O_OPEN)
574 {
575 if (!fFound)
576 rc = VERR_FILE_NOT_FOUND;
577 else
578 pIt->pfnComplete = pfnCompleted;
579 }
580 else
581 rc = VERR_INVALID_PARAMETER;
582
583 if (RT_SUCCESS(rc))
584 {
585 AssertPtr(pIt);
586 *ppStorage = pIt;
587 }
588
589 return rc;
590}
591
592static DECLCALLBACK(int) tstVDIoFileClose(void *pvUser, void *pStorage)
593{
594 PVDFILE pFile = (PVDFILE)pStorage;
595
596 /* Mark as not busy. */
597 pFile->pfnComplete = NULL;
598 return VINF_SUCCESS;
599}
600
601static DECLCALLBACK(int) tstVDIoFileDelete(void *pvUser, const char *pcszFilename)
602{
603 int rc = VINF_SUCCESS;
604 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
605 bool fFound = false;
606
607 /* Check if the file exists. */
608 PVDFILE pIt = NULL;
609 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
610 {
611 if (!RTStrCmp(pIt->pszName, pcszFilename))
612 {
613 fFound = true;
614 break;
615 }
616 }
617
618 if (fFound)
619 {
620 RTListNodeRemove(&pIt->Node);
621 VDMemDiskDestroy(pIt->pMemDisk);
622 RTStrFree(pIt->pszName);
623 RTMemFree(pIt);
624 }
625 else
626 rc = VERR_FILE_NOT_FOUND;
627
628 return rc;
629}
630
631static DECLCALLBACK(int) tstVDIoFileMove(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
632{
633 int rc = VINF_SUCCESS;
634 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
635 bool fFound = false;
636
637 /* Check if the file exists. */
638 PVDFILE pIt = NULL;
639 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
640 {
641 if (!RTStrCmp(pIt->pszName, pcszSrc))
642 {
643 fFound = true;
644 break;
645 }
646 }
647
648 if (fFound)
649 {
650 char *pszNew = RTStrDup(pcszDst);
651 if (pszNew)
652 {
653 RTStrFree(pIt->pszName);
654 pIt->pszName = pszNew;
655 }
656 else
657 rc = VERR_NO_MEMORY;
658 }
659 else
660 rc = VERR_FILE_NOT_FOUND;
661
662 return rc;
663}
664
665static DECLCALLBACK(int) tstVDIoFileGetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
666{
667 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
668
669 *pcbFreeSpace = ~0ULL; /** @todo: Implement */
670 return VINF_SUCCESS;
671}
672
673static DECLCALLBACK(int) tstVDIoFileGetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
674{
675 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
676
677 /** @todo: Implement */
678 return VINF_SUCCESS;
679}
680
681static DECLCALLBACK(int) tstVDIoFileGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
682{
683 PVDFILE pFile = (PVDFILE)pStorage;
684
685 return VDMemDiskGetSize(pFile->pMemDisk, pcbSize);
686}
687
688static DECLCALLBACK(int) tstVDIoFileSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
689{
690 PVDFILE pFile = (PVDFILE)pStorage;
691
692 return VDMemDiskSetSize(pFile->pMemDisk, cbSize);
693}
694
695static DECLCALLBACK(int) tstVDIoFileWriteSync(void *pvUser, void *pStorage, uint64_t uOffset,
696 const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten)
697{
698 int rc = VINF_SUCCESS;
699 PVDFILE pFile = (PVDFILE)pStorage;
700
701 RTSGBUF SgBuf;
702 RTSGSEG Seg;
703
704 Seg.pvSeg = (void *)pvBuffer;
705 Seg.cbSeg = cbBuffer;
706 RTSgBufInit(&SgBuf, &Seg, 1);
707 rc = VDMemDiskWrite(pFile->pMemDisk, uOffset, cbBuffer, &SgBuf);
708 if (RT_SUCCESS(rc) && pcbWritten)
709 *pcbWritten = cbBuffer;
710
711 return rc;
712}
713
714static DECLCALLBACK(int) tstVDIoFileReadSync(void *pvUser, void *pStorage, uint64_t uOffset,
715 void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
716{
717 int rc = VINF_SUCCESS;
718 PVDFILE pFile = (PVDFILE)pStorage;
719
720 RTSGBUF SgBuf;
721 RTSGSEG Seg;
722
723 Seg.pvSeg = pvBuffer;
724 Seg.cbSeg = cbBuffer;
725 RTSgBufInit(&SgBuf, &Seg, 1);
726 rc = VDMemDiskRead(pFile->pMemDisk, uOffset, cbBuffer, &SgBuf);
727 if (RT_SUCCESS(rc) && pcbRead)
728 *pcbRead = cbBuffer;
729
730 return rc;
731}
732
733static DECLCALLBACK(int) tstVDIoFileFlushSync(void *pvUser, void *pStorage)
734{
735 /* nothing to do. */
736 return VINF_SUCCESS;
737}
738
739static DECLCALLBACK(int) tstVDIoFileReadAsync(void *pvUser, void *pStorage, uint64_t uOffset,
740 PCRTSGSEG paSegments, size_t cSegments,
741 size_t cbRead, void *pvCompletion,
742 void **ppTask)
743{
744 int rc = VINF_SUCCESS;
745 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
746 PVDFILE pFile = (PVDFILE)pStorage;
747
748 rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pFile->pMemDisk, VDIOTXDIR_READ, uOffset,
749 cbRead, paSegments, cSegments, pFile->pfnComplete, pvCompletion);
750 if (RT_SUCCESS(rc))
751 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
752
753 return rc;
754}
755
756static DECLCALLBACK(int) tstVDIoFileWriteAsync(void *pvUser, void *pStorage, uint64_t uOffset,
757 PCRTSGSEG paSegments, size_t cSegments,
758 size_t cbWrite, void *pvCompletion,
759 void **ppTask)
760{
761 int rc = VINF_SUCCESS;
762 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
763 PVDFILE pFile = (PVDFILE)pStorage;
764
765 rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pFile->pMemDisk, VDIOTXDIR_WRITE, uOffset,
766 cbWrite, paSegments, cSegments, pFile->pfnComplete, pvCompletion);
767 if (RT_SUCCESS(rc))
768 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
769
770 return rc;
771}
772
773static DECLCALLBACK(int) tstVDIoFileFlushAsync(void *pvUser, void *pStorage, void *pvCompletion,
774 void **ppTask)
775{
776 int rc = VINF_SUCCESS;
777 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
778 PVDFILE pFile = (PVDFILE)pStorage;
779
780 rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pFile->pMemDisk, VDIOTXDIR_FLUSH, 0,
781 0, NULL, 0, pFile->pfnComplete, pvCompletion);
782 if (RT_SUCCESS(rc))
783 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
784
785 return rc;
786}
787
788/**
789 * Skips the characters until the given character is reached.
790 *
791 * @returns Start of the string with the given character
792 * or NULL if the string ended before.
793 *
794 * @param psz The string to skip.
795 * @param ch The character.
796 */
797static char *tstVDIoScriptSkipUntil(char *psz, char ch)
798{
799 while ( *psz != '\0'
800 && *psz != ch)
801 psz++;
802
803 return psz;
804}
805
806/**
807 * Skips the spaces of the current string.
808 *
809 * @returns Start of the string with a non space character
810 * or NULL if the string ended before.
811 *
812 * @param psz The string to skip.
813 */
814static char *tstVDIoScriptSkipSpace(char *psz)
815{
816 while ( *psz != '\0'
817 && RT_C_IS_SPACE(*psz))
818 psz++;
819
820 return psz;
821}
822
823/**
824 * Skips all characters until a space is reached of the current
825 * string.
826 *
827 * @returns Start of the string with a space character
828 * or NULL if the string ended before.
829 *
830 * @param psz The string to skip.
831 */
832static char *tstVDIoScriptSkipNonSpace(char *psz)
833{
834 while ( *psz != '\0'
835 && !RT_C_IS_SPACE(*psz))
836 psz++;
837
838 return psz;
839}
840
841/**
842 * Parses one argument name, value pair.
843 *
844 * @returns IPRT status code.
845 *
846 * @param pVDScriptAction Script action.
847 * @param pcszName Argument name.
848 * @param pcszValue Argument value.
849 * @param pScriptArg Where to fill in the parsed
850 * argument.
851 * @param pfMandatory Where to store whether the argument
852 * is mandatory.
853 */
854static int tstVDIoScriptArgumentParse(PCVDSCRIPTACTION pVDScriptAction, const char *pcszName,
855 const char *pcszValue, PVDSCRIPTARG pScriptArg, bool *pfMandatory)
856{
857 int rc = VERR_NOT_FOUND;
858
859 for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++)
860 {
861 if (!RTStrCmp(pVDScriptAction->paArgDesc[i].pcszName, pcszName))
862 {
863 rc = VINF_SUCCESS;
864
865 switch (pVDScriptAction->paArgDesc[i].enmType)
866 {
867 case VDSCRIPTARGTYPE_BOOL:
868 {
869 pScriptArg->enmType = VDSCRIPTARGTYPE_BOOL;
870 if (!RTStrICmp(pcszValue, "yes") || !RTStrICmp(pcszValue, "on"))
871 pScriptArg->u.fFlag = true;
872 else if (!RTStrICmp(pcszValue, "no") || !RTStrICmp(pcszValue, "off"))
873 pScriptArg->u.fFlag = false;
874 else
875 {
876 RTPrintf("Boolean argument malformed '%s'\n", pcszValue);
877 rc = VERR_INVALID_PARAMETER;
878 }
879 break;
880 }
881 case VDSCRIPTARGTYPE_SIGNED_NUMBER:
882 {
883 pScriptArg->enmType = VDSCRIPTARGTYPE_SIGNED_NUMBER;
884 AssertMsgFailed(("todo\n"));
885 break;
886 }
887 case VDSCRIPTARGTYPE_STRING:
888 {
889 pScriptArg->enmType = VDSCRIPTARGTYPE_STRING;
890 pScriptArg->u.pcszString = pcszValue;
891 break;
892 }
893 case VDSCRIPTARGTYPE_UNSIGNED_NUMBER:
894 {
895 char *pszSuffix = NULL;
896
897 pScriptArg->enmType = VDSCRIPTARGTYPE_UNSIGNED_NUMBER;
898 rc = RTStrToUInt64Ex(pcszValue, &pszSuffix, 10, &pScriptArg->u.u64);
899 if (rc == VWRN_TRAILING_CHARS)
900 {
901 switch (*pszSuffix)
902 {
903 case 'k':
904 case 'K':
905 {
906 pScriptArg->u.u64 *= 1024;
907 break;
908 }
909 case 'm':
910 case 'M':
911 {
912 pScriptArg->u.u64 *= 1024*1024;
913 break;
914 }
915 case 'g':
916 case 'G':
917 {
918 pScriptArg->u.u64 *= 1024*1024*1024;
919 break;
920 }
921 default:
922 {
923 RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
924 rc = VERR_INVALID_PARAMETER;
925 }
926 }
927 if (rc != VERR_INVALID_PARAMETER)
928 rc = VINF_SUCCESS;
929 }
930
931 break;
932 }
933 case VDSCRIPTARGTYPE_UNSIGNED_RANGE:
934 {
935 char *pszSuffix = NULL;
936
937 pScriptArg->enmType = VDSCRIPTARGTYPE_UNSIGNED_RANGE;
938 rc = RTStrToUInt64Ex(pcszValue, &pszSuffix, 10, &pScriptArg->u.Range.Start);
939 if (rc == VWRN_TRAILING_CHARS)
940 {
941 if (*pszSuffix != '-')
942 {
943 switch (*pszSuffix)
944 {
945 case 'k':
946 case 'K':
947 {
948 pScriptArg->u.Range.Start *= 1024;
949 break;
950 }
951 case 'm':
952 case 'M':
953 {
954 pScriptArg->u.Range.Start *= 1024*1024;
955 break;
956 }
957 case 'g':
958 case 'G':
959 {
960 pScriptArg->u.Range.Start *= 1024*1024*1024;
961 break;
962 }
963 default:
964 {
965 RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
966 rc = VERR_INVALID_PARAMETER;
967 }
968 }
969 }
970
971 if (*pszSuffix == '-')
972 {
973 pszSuffix++;
974 rc = RTStrToUInt64Ex(pszSuffix, &pszSuffix, 10, &pScriptArg->u.Range.End);
975 if (rc == VWRN_TRAILING_CHARS)
976 {
977 switch (*pszSuffix)
978 {
979 case 'k':
980 case 'K':
981 {
982 pScriptArg->u.Range.End *= 1024;
983 break;
984 }
985 case 'm':
986 case 'M':
987 {
988 pScriptArg->u.Range.End *= 1024*1024;
989 break;
990 }
991 case 'g':
992 case 'G':
993 {
994 pScriptArg->u.Range.End *= 1024*1024*1024;
995 break;
996 }
997 default:
998 {
999 RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
1000 rc = VERR_INVALID_PARAMETER;
1001 }
1002 }
1003 }
1004 }
1005 else
1006 rc = VERR_INVALID_PARAMETER;
1007 }
1008 else
1009 rc = VERR_INVALID_PARAMETER;
1010
1011 if (rc == VERR_INVALID_PARAMETER)
1012 RTPrintf("Invalid range format\n");
1013 break;
1014 }
1015 default:
1016 AssertMsgFailed(("Invalid script argument type\n"));
1017 }
1018
1019 if (RT_SUCCESS(rc))
1020 {
1021 pScriptArg->chId = pVDScriptAction->paArgDesc[i].chId;
1022 *pfMandatory = !!(pVDScriptAction->paArgDesc[i].fFlags & VDSCRIPTARGDESC_FLAG_MANDATORY);
1023 }
1024 break;
1025 }
1026 }
1027
1028 if (rc == VERR_NOT_FOUND)
1029 RTPrintf("Argument '%s' not found\n", pcszName);
1030
1031 return rc;
1032}
1033
1034/**
1035 * Parses the arguments of a action in the script.
1036 *
1037 * @returns IPRT status code.
1038 *
1039 * @param psz Argument string.
1040 * @param pVDScriptAction The script action to parses
1041 * arguments for.
1042 * @param paScriptArgs Where to store the arguments.
1043 * @param pcScriptArgs Where to store the actual number of
1044 * arguments parsed.
1045 */
1046static int tstVDIoScriptArgumentListParse(char *psz, PCVDSCRIPTACTION pVDScriptAction, PVDSCRIPTARG paScriptArgs, unsigned *pcScriptArgs)
1047{
1048 int rc = VINF_SUCCESS;
1049 unsigned cMandatoryArgsReq = 0;
1050 unsigned cScriptArgs = 0;
1051
1052 /* Count the number of mandatory arguments first. */
1053 for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++)
1054 if (pVDScriptAction->paArgDesc[i].fFlags & VDSCRIPTARGDESC_FLAG_MANDATORY)
1055 cMandatoryArgsReq++;
1056
1057 /* One argument is given in the form name=value. */
1058 *pcScriptArgs = 0;
1059
1060 while ( psz
1061 && *psz != '\0')
1062 {
1063 const char *pcszName = psz;
1064
1065 psz = tstVDIoScriptSkipUntil(psz, '=');
1066 if (psz != '\0')
1067 {
1068 *psz = '\0'; /* Overwrite */
1069 psz++;
1070 const char *pcszValue = psz;
1071
1072 psz = tstVDIoScriptSkipNonSpace(psz);
1073 if (psz != '\0')
1074 {
1075 *psz = '\0'; /* Overwrite */
1076 psz++;
1077 psz = tstVDIoScriptSkipSpace(psz);
1078
1079 /* We have the name and value pair now. */
1080 bool fMandatory;
1081 rc = tstVDIoScriptArgumentParse(pVDScriptAction, pcszName, pcszValue, &paScriptArgs[cScriptArgs], &fMandatory);
1082 if (RT_SUCCESS(rc))
1083 {
1084 if (fMandatory)
1085 cMandatoryArgsReq--;
1086 cScriptArgs++;
1087 }
1088 }
1089 else
1090 {
1091 RTPrintf("Value missing for argument '%s'\n", pcszName);
1092 rc = VERR_INVALID_STATE;
1093 break;
1094 }
1095 }
1096 else
1097 {
1098 RTPrintf("Argument in invalid form\n");
1099 rc = VERR_INVALID_STATE;
1100 break;
1101 }
1102 }
1103
1104 if ( RT_SUCCESS(rc)
1105 && cMandatoryArgsReq)
1106 {
1107 /* No arguments anymore but there are still mandatory arguments left. */
1108 RTPrintf("There are %u arguments missing for script action '%s\n", pVDScriptAction->pcszAction);
1109 rc = VERR_INVALID_STATE;
1110 }
1111
1112 if (RT_SUCCESS(rc))
1113 *pcScriptArgs = cScriptArgs;
1114
1115 return rc;
1116}
1117
1118/**
1119 * Executes the script pointed to by the given stream.
1120 *
1121 * @returns IPRT status code.
1122 *
1123 * @param pStrm The stream handle of the script.
1124 * @param pGlob Global test data.
1125 */
1126static int tstVDIoScriptExecute(PRTSTREAM pStrm, PVDTESTGLOB pGlob)
1127{
1128 int rc = VINF_SUCCESS;
1129 char abBuffer[0x1000]; /* Current assumption that a line is never longer than 4096 bytes. */
1130 PVDSCRIPTARG paScriptArgs = NULL;
1131 unsigned cScriptArgsMax = 0;
1132
1133 do
1134 {
1135 memset(abBuffer, 0, sizeof(abBuffer));
1136 rc = RTStrmGetLine(pStrm, abBuffer, sizeof(abBuffer));
1137 if (RT_SUCCESS(rc))
1138 {
1139 const char *pcszAction = NULL;
1140 char *psz = abBuffer;
1141
1142 /* Skip space */
1143 psz = tstVDIoScriptSkipSpace(psz);
1144 if (psz != '\0')
1145 {
1146 PCVDSCRIPTACTION pVDScriptAction = NULL;
1147
1148 /* Get the action name. */
1149 pcszAction = psz;
1150
1151 psz = tstVDIoScriptSkipNonSpace(psz);
1152 if (psz != '\0')
1153 {
1154 Assert(RT_C_IS_SPACE(*psz));
1155 *psz++ = '\0';
1156 }
1157
1158 /* Find the action. */
1159 for (unsigned i = 0; i < g_cScriptActions; i++)
1160 {
1161 if (!RTStrCmp(pcszAction, g_aScriptActions[i].pcszAction))
1162 {
1163 pVDScriptAction = &g_aScriptActions[i];
1164 break;
1165 }
1166 }
1167
1168 if (pVDScriptAction)
1169 {
1170 /* Parse arguments. */
1171 if (cScriptArgsMax < pVDScriptAction->cArgDescs)
1172 {
1173 /* Increase arguments array. */
1174 if (paScriptArgs)
1175 RTMemFree(paScriptArgs);
1176
1177 cScriptArgsMax = pVDScriptAction->cArgDescs;
1178 paScriptArgs = (PVDSCRIPTARG)RTMemAllocZ(cScriptArgsMax * sizeof(VDSCRIPTARG));
1179 if (paScriptArgs)
1180 {
1181 unsigned cScriptArgs;
1182
1183 rc = tstVDIoScriptArgumentListParse(psz, pVDScriptAction, paScriptArgs, &cScriptArgs);
1184 if (RT_SUCCESS(rc))
1185 {
1186 /* Execute the handler. */
1187 rc = pVDScriptAction->pfnHandler(pGlob, paScriptArgs, cScriptArgs);
1188 }
1189 }
1190 else
1191 {
1192 RTPrintf("Out of memory while allocating argument array for script action %s\n", pcszAction);
1193 rc = VERR_NO_MEMORY;
1194 }
1195 }
1196 }
1197 else
1198 {
1199 RTPrintf("Script action %s is not known\n", pcszAction);
1200 rc = VERR_NOT_FOUND;
1201 }
1202 }
1203 else
1204 {
1205 RTPrintf("Missing action name\n");
1206 rc = VERR_INVALID_STATE;
1207 }
1208 }
1209 } while(RT_SUCCESS(rc));
1210
1211 return rc;
1212}
1213
1214/**
1215 * Executes the given I/O script.
1216 *
1217 * @returns nothing.
1218 *
1219 * @param pcszFilename The script to execute.
1220 */
1221static void tstVDIoScriptRun(const char *pcszFilename)
1222{
1223 int rc = VINF_SUCCESS;
1224 PRTSTREAM pScriptStrm; /**< Stream of the script file. */
1225 VDTESTGLOB GlobTest; /**< Global test data. */
1226
1227 memset(&GlobTest, 0, sizeof(VDTESTGLOB));
1228 RTListInit(&GlobTest.ListFiles);
1229
1230 rc = RTStrmOpen(pcszFilename, "r", &pScriptStrm);
1231 if (RT_SUCCESS(rc))
1232 {
1233 /* Init global test data. */
1234 GlobTest.VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
1235 GlobTest.VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
1236 GlobTest.VDIErrorCallbacks.pfnError = tstVDError;
1237 GlobTest.VDIErrorCallbacks.pfnMessage = tstVDMessage;
1238
1239 rc = VDInterfaceAdd(&GlobTest.VDIError, "tstVDIo_VDIError", VDINTERFACETYPE_ERROR,
1240 &GlobTest.VDIErrorCallbacks, NULL, &GlobTest.pInterfacesDisk);
1241 AssertRC(rc);
1242
1243 GlobTest.VDIIoCallbacks.cbSize = sizeof(VDINTERFACEIO);
1244 GlobTest.VDIIoCallbacks.enmInterface = VDINTERFACETYPE_IO;
1245 GlobTest.VDIIoCallbacks.pfnOpen = tstVDIoFileOpen;
1246 GlobTest.VDIIoCallbacks.pfnClose = tstVDIoFileClose;
1247 GlobTest.VDIIoCallbacks.pfnDelete = tstVDIoFileDelete;
1248 GlobTest.VDIIoCallbacks.pfnMove = tstVDIoFileMove;
1249 GlobTest.VDIIoCallbacks.pfnGetFreeSpace = tstVDIoFileGetFreeSpace;
1250 GlobTest.VDIIoCallbacks.pfnGetModificationTime = tstVDIoFileGetModificationTime;
1251 GlobTest.VDIIoCallbacks.pfnGetSize = tstVDIoFileGetSize;
1252 GlobTest.VDIIoCallbacks.pfnSetSize = tstVDIoFileSetSize;
1253 GlobTest.VDIIoCallbacks.pfnWriteSync = tstVDIoFileWriteSync;
1254 GlobTest.VDIIoCallbacks.pfnReadSync = tstVDIoFileReadSync;
1255 GlobTest.VDIIoCallbacks.pfnFlushSync = tstVDIoFileFlushSync;
1256 GlobTest.VDIIoCallbacks.pfnReadAsync = tstVDIoFileReadAsync;
1257 GlobTest.VDIIoCallbacks.pfnWriteAsync = tstVDIoFileWriteAsync;
1258 GlobTest.VDIIoCallbacks.pfnFlushAsync = tstVDIoFileFlushAsync;
1259
1260 rc = VDInterfaceAdd(&GlobTest.VDIIo, "tstVDIo_VDIIo", VDINTERFACETYPE_IO,
1261 &GlobTest.VDIIoCallbacks, &GlobTest, &GlobTest.pInterfacesImages);
1262 AssertRC(rc);
1263
1264 /* Init I/O backend. */
1265 rc = VDIoBackendMemCreate(&GlobTest.pIoBackend);
1266 if (RT_SUCCESS(rc))
1267 {
1268 rc = VDCreate(GlobTest.pInterfacesDisk, VDTYPE_HDD, &GlobTest.pVD);
1269 if (RT_SUCCESS(rc))
1270 {
1271 /* Execute the script. */
1272 rc = tstVDIoScriptExecute(pScriptStrm, &GlobTest);
1273 if (RT_FAILURE(rc))
1274 {
1275 RTPrintf("Executing the script stream failed rc=%Rrc\n", rc);
1276 }
1277 }
1278 else
1279 RTPrintf("Failed to create disk container rc=%Rrc\n", rc);
1280 VDIoBackendMemDestroy(GlobTest.pIoBackend);
1281 }
1282 else
1283 RTPrintf("Creating the I/O backend failed rc=%Rrc\n");
1284
1285 RTStrmClose(pScriptStrm);
1286 }
1287 else
1288 RTPrintf("Opening script failed rc=%Rrc\n", rc);
1289}
1290
1291/**
1292 * Shows help message.
1293 */
1294static void printUsage(void)
1295{
1296 RTPrintf("Usage:\n"
1297 "--script <filename> Script to execute\n"
1298 "--replay <filename> Log to replay (not implemented yet)\n");
1299}
1300
1301static const RTGETOPTDEF g_aOptions[] =
1302{
1303 { "--script", 's', RTGETOPT_REQ_STRING },
1304 { "--replay", 'r', RTGETOPT_REQ_STRING },
1305};
1306
1307int main(int argc, char *argv[])
1308{
1309 RTR3Init();
1310 int rc;
1311 RTGETOPTUNION ValueUnion;
1312 RTGETOPTSTATE GetState;
1313 char c;
1314
1315 if (argc != 3)
1316 {
1317 printUsage();
1318 return RTEXITCODE_FAILURE;
1319 }
1320
1321 rc = VDInit();
1322 if (RT_FAILURE(rc))
1323 return RTEXITCODE_FAILURE;
1324
1325 RTGetOptInit(&GetState, argc, argv, g_aOptions,
1326 RT_ELEMENTS(g_aOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1327
1328 while ( RT_SUCCESS(rc)
1329 && (c = RTGetOpt(&GetState, &ValueUnion)))
1330 {
1331 switch (c)
1332 {
1333 case 's':
1334 tstVDIoScriptRun(ValueUnion.psz);
1335 break;
1336 case 'r':
1337 RTPrintf("Replaying I/O logs is not implemented yet\n");
1338 break;
1339 default:
1340 printUsage();
1341 }
1342 }
1343
1344 rc = VDShutdown();
1345 if (RT_FAILURE(rc))
1346 RTPrintf("tstVDIo: unloading backends failed! rc=%Rrc\n", rc);
1347
1348 return RTEXITCODE_SUCCESS;
1349}
1350
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