VirtualBox

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

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

tstVDIo: Add simple data verification.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 81.6 KB
Line 
1/* $Id: tstVDIo.cpp 36134 2011-03-02 23:31:06Z vboxsync $ */
2/** @file
3 *
4 * VBox HDD container test utility - I/O replay.
5 */
6
7/*
8 * Copyright (C) 2011 Oracle Corporation
9 *
10 * This file is part of VirtualBox Open Source Edition (OSE), as
11 * available from http://www.215389.xyz. This file is free software;
12 * you can redistribute it and/or modify it under the terms of the GNU
13 * General Public License (GPL) as published by the Free Software
14 * Foundation, in version 2 as it comes in the "COPYING" file of the
15 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
16 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17 */
18#define LOGGROUP LOGGROUP_DEFAULT
19#include <VBox/vd.h>
20#include <VBox/err.h>
21#include <VBox/log.h>
22#include <iprt/asm.h>
23#include <iprt/string.h>
24#include <iprt/stream.h>
25#include <iprt/mem.h>
26#include <iprt/initterm.h>
27#include <iprt/getopt.h>
28#include <iprt/list.h>
29#include <iprt/ctype.h>
30#include <iprt/semaphore.h>
31#include <iprt/thread.h>
32#include <iprt/rand.h>
33#include <iprt/critsect.h>
34
35#include "VDMemDisk.h"
36#include "VDIoBackendMem.h"
37#include "VDIoRnd.h"
38
39/**
40 * A virtual file backed by memory.
41 */
42typedef struct VDFILE
43{
44 /** Pointer to the next file. */
45 RTLISTNODE Node;
46 /** Name of the file. */
47 char *pszName;
48 /** Memory file baking the file. */
49 PVDMEMDISK pMemDisk;
50 /** Flag whether the file is read locked. */
51 bool fReadLock;
52 /** Flag whether the file is write locked. */
53 bool fWriteLock;
54} VDFILE, *PVDFILE;
55
56/**
57 * VD storage object.
58 */
59typedef struct VDSTORAGE
60{
61 /** Pointer to the file. */
62 PVDFILE pFile;
63 /** Completion callback of the VD layer. */
64 PFNVDCOMPLETED pfnComplete;
65} VDSTORAGE, *PVDSTORAGE;
66
67/**
68 * A virtual disk.
69 */
70typedef struct VDDISK
71{
72 /** List node. */
73 RTLISTNODE ListNode;
74 /** Name of the disk handle for identification. */
75 char *pszName;
76 /** HDD handle to operate on. */
77 PVBOXHDD pVD;
78 /** Memory disk used for data verification. */
79 PVDMEMDISK pMemDiskVerify;
80 /** Critical section to serialize access to the memory disk. */
81 RTCRITSECT CritSectVerify;
82 /** Physical CHS Geometry. */
83 VDGEOMETRY PhysGeom;
84 /** Logical CHS geometry. */
85 VDGEOMETRY LogicalGeom;
86} VDDISK, *PVDDISK;
87
88/**
89 * Global VD test state.
90 */
91typedef struct VDTESTGLOB
92{
93 /** List of active virtual disks. */
94 RTLISTNODE ListDisks;
95 /** Head of the active file list. */
96 RTLISTNODE ListFiles;
97 /** Memory I/O backend. */
98 PVDIOBACKENDMEM pIoBackend;
99 /** Error interface. */
100 VDINTERFACE VDIError;
101 /** Error interface callbacks. */
102 VDINTERFACEERROR VDIErrorCallbacks;
103 /** Pointer to the per disk interface list. */
104 PVDINTERFACE pInterfacesDisk;
105 /** I/O interface. */
106 VDINTERFACE VDIIo;
107 /** I/O interface callbacks. */
108 VDINTERFACEIO VDIIoCallbacks;
109 /** Pointer to the per image interface list. */
110 PVDINTERFACE pInterfacesImages;
111 /** I/O RNG handle. */
112 PVDIORND pIoRnd;
113} VDTESTGLOB, *PVDTESTGLOB;
114
115/**
116 * Transfer direction.
117 */
118typedef enum VDIOREQTXDIR
119{
120 VDIOREQTXDIR_READ = 0,
121 VDIOREQTXDIR_WRITE,
122 VDIOREQTXDIR_FLUSH
123} VDIOREQTXDIR;
124
125/**
126 * I/O request.
127 */
128typedef struct VDIOREQ
129{
130 /** Transfer type. */
131 VDIOREQTXDIR enmTxDir;
132 /** slot index. */
133 unsigned idx;
134 /** Start offset. */
135 uint64_t off;
136 /** Size to transfer. */
137 size_t cbReq;
138 /** S/G Buffer */
139 RTSGBUF SgBuf;
140 /** Data segment */
141 RTSGSEG DataSeg;
142 /** Flag whether the request is outstanding or not. */
143 volatile bool fOutstanding;
144 /** Buffer to use for reads. */
145 void *pvBufRead;
146 /** Opaque user data. */
147 void *pvUser;
148} VDIOREQ, *PVDIOREQ;
149
150/**
151 * I/O test data.
152 */
153typedef struct VDIOTEST
154{
155 /** Start offset. */
156 uint64_t offStart;
157 /** End offset. */
158 uint64_t offEnd;
159 /** Flag whether random or sequential access is wanted */
160 bool fRandomAccess;
161 /** Block size. */
162 size_t cbBlkIo;
163 /** Number of bytes to transfer. */
164 uint64_t cbIo;
165 /** Chance in percent to get a write. */
166 unsigned uWriteChance;
167 /** Pointer to the I/O data generator. */
168 PVDIORND pIoRnd;
169 /** Data dependent on the I/O mode (sequential or random). */
170 union
171 {
172 /** Next offset for sequential access. */
173 uint64_t offNext;
174 /** Data for random acess. */
175 struct
176 {
177 /** Number of valid entries in the bitmap. */
178 uint32_t cBlocks;
179 /** Pointer to the bitmap marking accessed blocks. */
180 uint8_t *pbMapAccessed;
181 /** Number of unaccessed blocks. */
182 uint32_t cBlocksLeft;
183 } Rnd;
184 } u;
185} VDIOTEST, *PVDIOTEST;
186
187/**
188 * Argument types.
189 */
190typedef enum VDSCRIPTARGTYPE
191{
192 /** Argument is a string. */
193 VDSCRIPTARGTYPE_STRING = 0,
194 /** Argument is a 64bit unsigned number. */
195 VDSCRIPTARGTYPE_UNSIGNED_NUMBER,
196 /** Argument is a 64bit signed number. */
197 VDSCRIPTARGTYPE_SIGNED_NUMBER,
198 /** Arugment is a unsigned 64bit range */
199 VDSCRIPTARGTYPE_UNSIGNED_RANGE,
200 /** Arugment is a boolean. */
201 VDSCRIPTARGTYPE_BOOL
202} VDSCRIPTARGTYPE;
203
204/**
205 * Script argument.
206 */
207typedef struct VDSCRIPTARG
208{
209 /** Argument identifier. */
210 char chId;
211 /** Type of the argument. */
212 VDSCRIPTARGTYPE enmType;
213 /** Type depndent data. */
214 union
215 {
216 /** String. */
217 const char *pcszString;
218 /** Bool. */
219 bool fFlag;
220 /** unsigned number. */
221 uint64_t u64;
222 /** Signed number. */
223 int64_t i64;
224 /** Unsigned range. */
225 struct
226 {
227 uint64_t Start;
228 uint64_t End;
229 } Range;
230 } u;
231} VDSCRIPTARG, *PVDSCRIPTARG;
232
233/** Script action handler. */
234typedef DECLCALLBACK(int) FNVDSCRIPTACTION(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
235/** Pointer to a script action handler. */
236typedef FNVDSCRIPTACTION *PFNVDSCRIPTACTION;
237
238/**
239 * Script argument descriptor.
240 */
241typedef struct VDSCRIPTARGDESC
242{
243 /** Name of the arugment. */
244 const char *pcszName;
245 /** Identifier for the argument. */
246 char chId;
247 /** Type of the argument. */
248 VDSCRIPTARGTYPE enmType;
249 /** Flags */
250 uint32_t fFlags;
251} VDSCRIPTARGDESC, *PVDSCRIPTARGDESC;
252/** Pointer to a const script argument descriptor. */
253typedef const VDSCRIPTARGDESC *PCVDSCRIPTARGDESC;
254
255/** Flag whether the argument is mandatory. */
256#define VDSCRIPTARGDESC_FLAG_MANDATORY RT_BIT(0)
257/** Flag whether the number can have a size suffix (K|M|G) */
258#define VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX RT_BIT(1)
259
260/**
261 * Script action.
262 */
263typedef struct VDSCRIPTACTION
264{
265 /** Action name. */
266 const char *pcszAction;
267 /** Pointer to the arguments. */
268 const PCVDSCRIPTARGDESC paArgDesc;
269 /** Number of arugments in the array. */
270 unsigned cArgDescs;
271 /** Pointer to the action handler. */
272 PFNVDSCRIPTACTION pfnHandler;
273} VDSCRIPTACTION, *PVDSCRIPTACTION;
274
275typedef const VDSCRIPTACTION *PCVDSCRIPTACTION;
276
277static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
278static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
279static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
280static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
281static DECLCALLBACK(int) vdScriptHandlerMerge(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
282static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
283static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
284static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
285static DECLCALLBACK(int) vdScriptHandlerSleep(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
286static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
287static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
288static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
289static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
290static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs);
291
292/* create action */
293const VDSCRIPTARGDESC g_aArgCreate[] =
294{
295 /* pcszName chId enmType fFlags */
296 {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
297 {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
298 {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
299 {"type", 't', VDSCRIPTARGTYPE_STRING, 0},
300 {"backend", 'b', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
301 {"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX}
302};
303
304/* open action */
305const VDSCRIPTARGDESC g_aArgOpen[] =
306{
307 /* pcszName chId enmType fFlags */
308 {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
309 {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
310 {"backend", 'b', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
311 {"shareable", 's', VDSCRIPTARGTYPE_BOOL, 0},
312 {"readonly", 'r', VDSCRIPTARGTYPE_BOOL, 0}
313};
314
315/* I/O action */
316const VDSCRIPTARGDESC g_aArgIo[] =
317{
318 /* pcszName chId enmType fFlags */
319 {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
320 {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0},
321 {"max-reqs", 'l', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0},
322 {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
323 {"size", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
324 {"blocksize", 'b', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
325 {"off", 'o', VDSCRIPTARGTYPE_UNSIGNED_RANGE, VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
326 {"writes", 'w', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY}
327};
328
329/* flush action */
330const VDSCRIPTARGDESC g_aArgFlush[] =
331{
332 /* pcszName chId enmType fFlags */
333 {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
334 {"async", 'a', VDSCRIPTARGTYPE_BOOL, 0}
335};
336
337/* merge action */
338const VDSCRIPTARGDESC g_aArgMerge[] =
339{
340 /* pcszName chId enmType fFlags */
341 {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
342 {"forward", 'f', VDSCRIPTARGTYPE_BOOL, VDSCRIPTARGDESC_FLAG_MANDATORY}
343};
344
345/* close action */
346const VDSCRIPTARGDESC g_aArgClose[] =
347{
348 /* pcszName chId enmType fFlags */
349 {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
350 {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
351 {"delete", 'r', VDSCRIPTARGTYPE_BOOL, VDSCRIPTARGDESC_FLAG_MANDATORY}
352};
353
354/* I/O RNG create action */
355const VDSCRIPTARGDESC g_aArgIoRngCreate[] =
356{
357 /* pcszName chId enmType fFlags */
358 {"size", 'd', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY | VDSCRIPTARGDESC_FLAG_SIZE_SUFFIX},
359 {"mode", 'm', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
360 {"seed", 's', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, 0}
361};
362
363/* Sleep */
364const VDSCRIPTARGDESC g_aArgSleep[] =
365{
366 /* pcszName chId enmType fFlags */
367 {"time", 't', VDSCRIPTARGTYPE_UNSIGNED_NUMBER, VDSCRIPTARGDESC_FLAG_MANDATORY}
368};
369
370/* Dump memory file */
371const VDSCRIPTARGDESC g_aArgDumpFile[] =
372{
373 /* pcszName chId enmType fFlags */
374 {"file", 'f', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
375 {"path", 'p', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
376};
377
378/* Create virtual disk handle */
379const VDSCRIPTARGDESC g_aArgCreateDisk[] =
380{
381 /* pcszName chId enmType fFlags */
382 {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
383 {"verify", 'v', VDSCRIPTARGTYPE_BOOL, 0}
384};
385
386/* Create virtual disk handle */
387const VDSCRIPTARGDESC g_aArgDestroyDisk[] =
388{
389 /* pcszName chId enmType fFlags */
390 {"name", 'n', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
391};
392
393/* Compare virtual disks */
394const VDSCRIPTARGDESC g_aArgCompareDisks[] =
395{
396 /* pcszName chId enmType fFlags */
397 {"disk1", '1', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
398 {"disk2", '2', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY}
399};
400
401/* Dump disk info */
402const VDSCRIPTARGDESC g_aArgDumpDiskInfo[] =
403{
404 /* pcszName chId enmType fFlags */
405 {"disk", 'd', VDSCRIPTARGTYPE_STRING, VDSCRIPTARGDESC_FLAG_MANDATORY},
406};
407
408const VDSCRIPTACTION g_aScriptActions[] =
409{
410 /* pcszAction paArgDesc cArgDescs pfnHandler */
411 {"create", g_aArgCreate, RT_ELEMENTS(g_aArgCreate), vdScriptHandlerCreate},
412 {"open", g_aArgOpen, RT_ELEMENTS(g_aArgOpen), vdScriptHandlerOpen},
413 {"io", g_aArgIo, RT_ELEMENTS(g_aArgIo), vdScriptHandlerIo},
414 {"flush", g_aArgFlush, RT_ELEMENTS(g_aArgFlush), vdScriptHandlerFlush},
415 {"close", g_aArgClose, RT_ELEMENTS(g_aArgClose), vdScriptHandlerClose},
416 {"merge", g_aArgMerge, RT_ELEMENTS(g_aArgMerge), vdScriptHandlerMerge},
417 {"iorngcreate", g_aArgIoRngCreate, RT_ELEMENTS(g_aArgIoRngCreate), vdScriptHandlerIoRngCreate},
418 {"iorngdestroy", NULL, 0, vdScriptHandlerIoRngDestroy},
419 {"sleep", g_aArgSleep, RT_ELEMENTS(g_aArgSleep), vdScriptHandlerSleep},
420 {"dumpfile", g_aArgDumpFile, RT_ELEMENTS(g_aArgDumpFile), vdScriptHandlerDumpFile},
421 {"createdisk", g_aArgCreateDisk, RT_ELEMENTS(g_aArgCreateDisk), vdScriptHandlerCreateDisk},
422 {"destroydisk", g_aArgDestroyDisk, RT_ELEMENTS(g_aArgDestroyDisk), vdScriptHandlerDestroyDisk},
423 {"comparedisks", g_aArgCompareDisks, RT_ELEMENTS(g_aArgCompareDisks), vdScriptHandlerCompareDisks},
424 {"dumpdiskinfo", g_aArgDumpDiskInfo, RT_ELEMENTS(g_aArgDumpDiskInfo), vdScriptHandlerDumpDiskInfo},
425};
426
427const unsigned g_cScriptActions = RT_ELEMENTS(g_aScriptActions);
428
429static void tstVDError(void *pvUser, int rc, RT_SRC_POS_DECL,
430 const char *pszFormat, va_list va)
431{
432 RTPrintf("tstVD: Error %Rrc at %s:%u (%s): ", rc, RT_SRC_POS_ARGS);
433 RTPrintfV(pszFormat, va);
434 RTPrintf("\n");
435}
436
437static int tstVDMessage(void *pvUser, const char *pszFormat, va_list va)
438{
439 RTPrintf("tstVD: ");
440 RTPrintfV(pszFormat, va);
441 return VINF_SUCCESS;
442}
443
444static int tstVDIoTestInit(PVDIOTEST pIoTest, PVDTESTGLOB pGlob, bool fRandomAcc, uint64_t cbIo,
445 size_t cbBlkSize, uint64_t offStart, uint64_t offEnd,
446 unsigned uWriteChance);
447static bool tstVDIoTestRunning(PVDIOTEST pIoTest);
448static void tstVDIoTestDestroy(PVDIOTEST pIoTest);
449static bool tstVDIoTestReqOutstanding(PVDIOREQ pIoReq);
450static int tstVDIoTestReqInit(PVDIOTEST pIoTest, PVDIOREQ pIoReq, void *pvUser);
451static void tstVDIoTestReqComplete(void *pvUser1, void *pvUser2, int rcReq);
452
453static PVDDISK tstVDIoGetDiskByName(PVDTESTGLOB pGlob, const char *pcszDisk);
454
455static DECLCALLBACK(int) vdScriptHandlerCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
456{
457 int rc = VINF_SUCCESS;
458 uint64_t cbSize = 0;
459 const char *pcszBackend = NULL;
460 const char *pcszImage = NULL;
461 const char *pcszDisk = NULL;
462 PVDDISK pDisk = NULL;
463 bool fBase = false;
464 bool fDynamic = true;
465
466 for (unsigned i = 0; i < cScriptArgs; i++)
467 {
468 switch (paScriptArgs[i].chId)
469 {
470 case 'd':
471 {
472 pcszDisk = paScriptArgs[i].u.pcszString;
473 break;
474 }
475 case 'm':
476 {
477 if (!RTStrICmp(paScriptArgs[i].u.pcszString, "base"))
478 fBase = true;
479 else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "diff"))
480 fBase = false;
481 else
482 {
483 RTPrintf("Invalid image mode '%s' given\n", paScriptArgs[i].u.pcszString);
484 rc = VERR_INVALID_PARAMETER;
485 }
486 break;
487 }
488 case 'n':
489 {
490 pcszImage = paScriptArgs[i].u.pcszString;
491 break;
492 }
493 case 'b':
494 {
495 pcszBackend = paScriptArgs[i].u.pcszString;
496 break;
497 }
498 case 's':
499 {
500 cbSize = paScriptArgs[i].u.u64;
501 break;
502 }
503 case 't':
504 {
505 if (!RTStrICmp(paScriptArgs[i].u.pcszString, "fixed"))
506 fDynamic = false;
507 else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "dynamic"))
508 fDynamic = true;
509 else
510 {
511 RTPrintf("Invalid image type '%s' given\n", paScriptArgs[i].u.pcszString);
512 rc = VERR_INVALID_PARAMETER;
513 }
514 break;
515 }
516 default:
517 AssertMsgFailed(("Invalid argument given!\n"));
518 }
519
520 if (RT_FAILURE(rc))
521 break;
522 }
523
524 if (RT_SUCCESS(rc))
525 {
526 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
527 if (pDisk)
528 {
529 unsigned fImageFlags = VD_IMAGE_FLAGS_NONE;
530
531 if (!fDynamic)
532 fImageFlags |= VD_IMAGE_FLAGS_FIXED;
533
534 if (fBase)
535 rc = VDCreateBase(pDisk->pVD, pcszBackend, pcszImage, cbSize, fImageFlags, NULL,
536 &pDisk->PhysGeom, &pDisk->LogicalGeom,
537 NULL, VD_OPEN_FLAGS_ASYNC_IO, pGlob->pInterfacesImages, NULL);
538 else
539 rc = VDCreateDiff(pDisk->pVD, pcszBackend, pcszImage, fImageFlags, NULL, NULL, NULL, VD_OPEN_FLAGS_ASYNC_IO,
540 pGlob->pInterfacesImages, NULL);
541 }
542 else
543 rc = VERR_NOT_FOUND;
544 }
545
546 return rc;
547}
548
549static DECLCALLBACK(int) vdScriptHandlerOpen(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
550{
551 int rc = VINF_SUCCESS;
552 const char *pcszBackend = NULL;
553 const char *pcszImage = NULL;
554 const char *pcszDisk = NULL;
555 PVDDISK pDisk = NULL;
556 bool fShareable = false;
557 bool fReadonly = false;
558
559 for (unsigned i = 0; i < cScriptArgs; i++)
560 {
561 switch (paScriptArgs[i].chId)
562 {
563 case 'd':
564 {
565 pcszDisk = paScriptArgs[i].u.pcszString;
566 break;
567 }
568 case 'n':
569 {
570 pcszImage = paScriptArgs[i].u.pcszString;
571 break;
572 }
573 case 'b':
574 {
575 pcszBackend = paScriptArgs[i].u.pcszString;
576 break;
577 }
578 case 's':
579 {
580 fShareable = paScriptArgs[i].u.fFlag;
581 break;
582 }
583 case 'r':
584 {
585 fReadonly = paScriptArgs[i].u.fFlag;
586 break;
587 }
588 default:
589 AssertMsgFailed(("Invalid argument given!\n"));
590 }
591
592 if (RT_FAILURE(rc))
593 break;
594 }
595
596 if (RT_SUCCESS(rc))
597 {
598 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
599 if (pDisk)
600 {
601 unsigned fOpenFlags = VD_OPEN_FLAGS_ASYNC_IO;
602
603 if (fShareable)
604 fOpenFlags |= VD_OPEN_FLAGS_SHAREABLE;
605 if (fReadonly)
606 fOpenFlags |= VD_OPEN_FLAGS_READONLY;
607
608 rc = VDOpen(pDisk->pVD, pcszBackend, pcszImage, fOpenFlags, pGlob->pInterfacesImages);
609 }
610 else
611 rc = VERR_NOT_FOUND;
612 }
613
614 return rc;
615}
616
617static DECLCALLBACK(int) vdScriptHandlerIo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
618{
619 int rc = VINF_SUCCESS;
620 bool fAsync = false;
621 bool fRandomAcc = false;
622 uint64_t cbIo = 0;
623 uint64_t cbBlkSize = 0;
624 bool fDataProviderRnd = false;
625 bool fPrintStats = false;
626 uint64_t offStart = 0;
627 uint64_t offEnd = 0;
628 unsigned cMaxReqs = 0;
629 uint8_t uWriteChance = 0;
630 const char *pcszDisk = NULL;
631 PVDDISK pDisk = NULL;
632
633 for (unsigned i = 0; i < cScriptArgs; i++)
634 {
635 switch (paScriptArgs[i].chId)
636 {
637 case 'd':
638 {
639 pcszDisk = paScriptArgs[i].u.pcszString;
640 break;
641 }
642 case 'a':
643 {
644 fAsync = paScriptArgs[i].u.fFlag;
645 break;
646 }
647 case 'l':
648 {
649 cMaxReqs = paScriptArgs[i].u.u64;
650 break;
651 }
652 case 'm':
653 {
654 if (!RTStrICmp(paScriptArgs[i].u.pcszString, "seq"))
655 fRandomAcc = false;
656 else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "rnd"))
657 fRandomAcc = true;
658 else
659 {
660 RTPrintf("Invalid access mode '%s'\n", paScriptArgs[i].u.pcszString);
661 rc = VERR_INVALID_PARAMETER;
662 }
663 break;
664 }
665 case 's':
666 {
667 cbIo = paScriptArgs[i].u.u64;
668 break;
669 }
670 case 'b':
671 {
672 cbBlkSize = paScriptArgs[i].u.u64;
673 break;
674 }
675 case 'o':
676 {
677 offStart = paScriptArgs[i].u.Range.Start;
678 offEnd = paScriptArgs[i].u.Range.End;
679 break;
680 }
681 case 'w':
682 {
683 uWriteChance = (uint8_t)paScriptArgs[i].u.u64;
684 break;
685 }
686 default:
687 AssertMsgFailed(("Invalid argument given!\n"));
688 }
689
690 if (RT_FAILURE(rc))
691 break;
692 }
693
694 if (RT_SUCCESS(rc))
695 {
696 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
697 if (!pDisk)
698 rc = VERR_NOT_FOUND;
699 }
700
701 if (RT_SUCCESS(rc))
702 {
703 /* Set defaults if not set by the user. */
704 if (offStart == 0 && offEnd == 0)
705 {
706 offEnd = VDGetSize(pDisk->pVD, VD_LAST_IMAGE);
707 if (offEnd == 0)
708 return VERR_INVALID_STATE;
709 }
710
711 if (!cbIo)
712 cbIo = offEnd;
713 }
714
715 if (RT_SUCCESS(rc))
716 {
717 VDIOTEST IoTest;
718
719 rc = tstVDIoTestInit(&IoTest, pGlob, fRandomAcc, cbIo, cbBlkSize, offStart, offEnd, uWriteChance);
720 if (RT_SUCCESS(rc))
721 {
722 PVDIOREQ paIoReq = NULL;
723 unsigned cMaxTasksOutstanding = fAsync ? cMaxReqs : 1;
724 RTSEMEVENT EventSem;
725
726 rc = RTSemEventCreate(&EventSem);
727 paIoReq = (PVDIOREQ)RTMemAllocZ(cMaxTasksOutstanding * sizeof(VDIOREQ));
728 if (paIoReq && RT_SUCCESS(rc))
729 {
730 uint64_t NanoTS = RTTimeNanoTS();
731
732 /* Init requests. */
733 for (unsigned i = 0; i < cMaxTasksOutstanding; i++)
734 {
735 paIoReq[i].idx = i;
736 paIoReq[i].pvBufRead = RTMemAlloc(cbBlkSize);
737 if (!paIoReq[i].pvBufRead)
738 {
739 rc = VERR_NO_MEMORY;
740 break;
741 }
742 }
743
744 while ( tstVDIoTestRunning(&IoTest)
745 && RT_SUCCESS(rc))
746 {
747 bool fTasksOutstanding = false;
748 unsigned idx = 0;
749
750 /* Submit all idling requests. */
751 while ( idx < cMaxTasksOutstanding
752 && tstVDIoTestRunning(&IoTest))
753 {
754 if (!tstVDIoTestReqOutstanding(&paIoReq[idx]))
755 {
756 rc = tstVDIoTestReqInit(&IoTest, &paIoReq[idx], pDisk);
757 AssertRC(rc);
758
759 if (RT_SUCCESS(rc))
760 {
761 if (!fAsync)
762 {
763 switch (paIoReq[idx].enmTxDir)
764 {
765 case VDIOREQTXDIR_READ:
766 {
767 rc = VDRead(pDisk->pVD, paIoReq[idx].off, paIoReq[idx].DataSeg.pvSeg, paIoReq[idx].cbReq);
768
769 if (RT_SUCCESS(rc)
770 && pDisk->pMemDiskVerify)
771 {
772 RTSGBUF SgBuf;
773 RTSgBufInit(&SgBuf, &paIoReq[idx].DataSeg, 1);
774
775 if (VDMemDiskCmp(pDisk->pMemDiskVerify, paIoReq[idx].off, paIoReq[idx].cbReq, &SgBuf))
776 {
777 RTPrintf("Corrupted disk at offset %llu!\n", paIoReq[idx].off);
778 rc = VERR_INVALID_STATE;
779 }
780 }
781 break;
782 }
783 case VDIOREQTXDIR_WRITE:
784 {
785 rc = VDWrite(pDisk->pVD, paIoReq[idx].off, paIoReq[idx].DataSeg.pvSeg, paIoReq[idx].cbReq);
786
787 if (RT_SUCCESS(rc)
788 && pDisk->pMemDiskVerify)
789 {
790 RTSGBUF SgBuf;
791 RTSgBufInit(&SgBuf, &paIoReq[idx].DataSeg, 1);
792 rc = VDMemDiskWrite(pDisk->pMemDiskVerify, paIoReq[idx].off, paIoReq[idx].cbReq, &SgBuf);
793 }
794 break;
795 }
796 case VDIOREQTXDIR_FLUSH:
797 {
798 rc = VDFlush(pDisk->pVD);
799 break;
800 }
801 }
802 if (RT_SUCCESS(rc))
803 idx++;
804 }
805 else
806 {
807 switch (paIoReq[idx].enmTxDir)
808 {
809 case VDIOREQTXDIR_READ:
810 {
811 rc = VDAsyncRead(pDisk->pVD, paIoReq[idx].off, paIoReq[idx].cbReq, &paIoReq[idx].SgBuf,
812 tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
813 break;
814 }
815 case VDIOREQTXDIR_WRITE:
816 {
817 rc = VDAsyncWrite(pDisk->pVD, paIoReq[idx].off, paIoReq[idx].cbReq, &paIoReq[idx].SgBuf,
818 tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
819 break;
820 }
821 case VDIOREQTXDIR_FLUSH:
822 {
823 rc = VDAsyncFlush(pDisk->pVD, tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
824 break;
825 }
826 }
827
828 if (rc == VERR_VD_ASYNC_IO_IN_PROGRESS)
829 {
830 idx++;
831 fTasksOutstanding = true;
832 rc = VINF_SUCCESS;
833 }
834 else if (rc == VINF_VD_ASYNC_IO_FINISHED)
835 {
836 switch (paIoReq[idx].enmTxDir)
837 {
838 case VDIOREQTXDIR_READ:
839 {
840 rc = VDAsyncRead(pDisk->pVD, paIoReq[idx].off, paIoReq[idx].cbReq, &paIoReq[idx].SgBuf,
841 tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
842 if (pDisk->pMemDiskVerify)
843 {
844 RTCritSectEnter(&pDisk->CritSectVerify);
845 RTSgBufReset(&paIoReq[idx].SgBuf);
846
847 if (VDMemDiskCmp(pDisk->pMemDiskVerify, paIoReq[idx].off, paIoReq[idx].cbReq,
848 &paIoReq[idx].SgBuf))
849 {
850 RTPrintf("Corrupted disk at offset %llu!\n", paIoReq[idx].off);
851 rc = VERR_INVALID_STATE;
852 }
853 RTCritSectLeave(&pDisk->CritSectVerify);
854 }
855 }
856 case VDIOREQTXDIR_WRITE:
857 {
858 rc = VDAsyncWrite(pDisk->pVD, paIoReq[idx].off, paIoReq[idx].cbReq, &paIoReq[idx].SgBuf,
859 tstVDIoTestReqComplete, &paIoReq[idx], EventSem);
860
861 if (pDisk->pMemDiskVerify)
862 {
863 RTCritSectEnter(&pDisk->CritSectVerify);
864 RTSgBufReset(&paIoReq[idx].SgBuf);
865
866 rc = VDMemDiskWrite(pDisk->pMemDiskVerify, paIoReq[idx].off, paIoReq[idx].cbReq,
867 &paIoReq[idx].SgBuf);
868 RTCritSectLeave(&pDisk->CritSectVerify);
869 }
870 break;
871 }
872 case VDIOREQTXDIR_FLUSH:
873 break;
874 }
875
876 ASMAtomicXchgBool(&paIoReq[idx].fOutstanding, false);
877 if (rc != VERR_INVALID_STATE)
878 rc = VINF_SUCCESS;
879 }
880 }
881
882 if (RT_FAILURE(rc))
883 RTPrintf("Error submitting task %u rc=%Rrc\n", paIoReq[idx].idx, rc);
884 }
885 }
886 }
887
888 /* Wait for a request to complete. */
889 if ( fAsync
890 && fTasksOutstanding)
891 {
892 rc = RTSemEventWait(EventSem, RT_INDEFINITE_WAIT);
893 AssertRC(rc);
894 }
895 }
896
897 /* Cleanup, wait for all tasks to complete. */
898 while (fAsync)
899 {
900 unsigned idx = 0;
901 bool fAllIdle = true;
902
903 while (idx < cMaxTasksOutstanding)
904 {
905 if (tstVDIoTestReqOutstanding(&paIoReq[idx]))
906 {
907 fAllIdle = false;
908 break;
909 }
910 idx++;
911 }
912
913 if (!fAllIdle)
914 {
915 rc = RTSemEventWait(EventSem, 100);
916 Assert(RT_SUCCESS(rc) || rc == VERR_TIMEOUT);
917 }
918 else
919 break;
920 }
921
922 NanoTS = RTTimeNanoTS() - NanoTS;
923 uint64_t SpeedKBs = (uint64_t)(cbIo / (NanoTS / 1000000000.0) / 1024);
924 RTPrintf("I/O Test: Throughput %lld kb/s\n", SpeedKBs);
925
926 RTSemEventDestroy(EventSem);
927 RTMemFree(paIoReq);
928 }
929 else
930 rc = VERR_NO_MEMORY;
931
932 tstVDIoTestDestroy(&IoTest);
933 }
934 }
935
936 return rc;
937}
938
939static DECLCALLBACK(int) vdScriptHandlerFlush(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
940{
941 int rc = VINF_SUCCESS;
942 bool fAsync = false;
943 const char *pcszDisk = NULL;
944 PVDDISK pDisk = NULL;
945
946 for (unsigned i = 0; i < cScriptArgs; i++)
947 {
948 switch (paScriptArgs[i].chId)
949 {
950 case 'd':
951 {
952 pcszDisk = paScriptArgs[i].u.pcszString;
953 break;
954 }
955 case 'a':
956 {
957 fAsync = paScriptArgs[i].u.fFlag;
958 break;
959 }
960
961 default:
962 AssertMsgFailed(("Invalid argument given!\n"));
963 }
964
965 if (RT_FAILURE(rc))
966 break;
967 }
968
969 if (RT_SUCCESS(rc))
970 {
971 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
972 if (!pDisk)
973 rc = VERR_NOT_FOUND;
974 else if (fAsync)
975 {
976 /** @todo */
977 rc = VERR_NOT_IMPLEMENTED;
978 }
979 else
980 rc = VDFlush(pDisk->pVD);
981 }
982
983 return rc;
984}
985
986static DECLCALLBACK(int) vdScriptHandlerMerge(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
987{
988 return VERR_NOT_IMPLEMENTED;
989}
990
991static DECLCALLBACK(int) vdScriptHandlerClose(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
992{
993 int rc = VINF_SUCCESS;
994 bool fAll = false;
995 bool fDelete = false;
996 const char *pcszDisk = NULL;
997 PVDDISK pDisk = NULL;
998
999 for (unsigned i = 0; i < cScriptArgs; i++)
1000 {
1001 switch (paScriptArgs[i].chId)
1002 {
1003 case 'd':
1004 {
1005 pcszDisk = paScriptArgs[i].u.pcszString;
1006 break;
1007 }
1008 case 'm':
1009 {
1010 if (!RTStrICmp(paScriptArgs[i].u.pcszString, "all"))
1011 fAll = true;
1012 else if (!RTStrICmp(paScriptArgs[i].u.pcszString, "single"))
1013 fAll = false;
1014 else
1015 {
1016 RTPrintf("Invalid mode '%s' given\n", paScriptArgs[i].u.pcszString);
1017 rc = VERR_INVALID_PARAMETER;
1018 }
1019 break;
1020 }
1021 case 'r':
1022 {
1023 fDelete = paScriptArgs[i].u.fFlag;
1024 break;
1025 }
1026 default:
1027 AssertMsgFailed(("Invalid argument given!\n"));
1028 }
1029
1030 if (RT_FAILURE(rc))
1031 break;
1032 }
1033
1034 if ( RT_SUCCESS(rc)
1035 && fAll
1036 && fDelete)
1037 {
1038 RTPrintf("mode=all doesn't work with delete=yes\n");
1039 rc = VERR_INVALID_PARAMETER;
1040 }
1041
1042 if (RT_SUCCESS(rc))
1043 {
1044 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1045 if (pDisk)
1046 {
1047 if (fAll)
1048 rc = VDCloseAll(pDisk->pVD);
1049 else
1050 rc = VDClose(pDisk->pVD, fDelete);
1051 }
1052 else
1053 rc = VERR_NOT_FOUND;
1054 }
1055 return rc;
1056}
1057
1058
1059static DECLCALLBACK(int) vdScriptHandlerIoRngCreate(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1060{
1061 int rc = VINF_SUCCESS;
1062 size_t cbPattern = 0;
1063 uint64_t uSeed = 0;
1064 const char *pcszSeeder = NULL;
1065
1066 for (unsigned i = 0; i < cScriptArgs; i++)
1067 {
1068 switch (paScriptArgs[i].chId)
1069 {
1070 case 'd':
1071 {
1072 cbPattern = paScriptArgs[i].u.u64;
1073 break;
1074 }
1075 case 's':
1076 {
1077 uSeed = paScriptArgs[i].u.u64;
1078 break;
1079 }
1080 case 'm':
1081 {
1082 pcszSeeder = paScriptArgs[i].u.pcszString;
1083 break;
1084 }
1085 default:
1086 AssertMsgFailed(("Invalid argument given!\n"));
1087 }
1088 }
1089
1090 if (pGlob->pIoRnd)
1091 {
1092 RTPrintf("I/O RNG already exists\n");
1093 rc = VERR_INVALID_STATE;
1094 }
1095 else
1096 {
1097 uint64_t uSeedToUse = 0;
1098
1099 if (!RTStrICmp(pcszSeeder, "manual"))
1100 uSeedToUse = uSeed;
1101 else if (!RTStrICmp(pcszSeeder, "time"))
1102 uSeedToUse = RTTimeSystemMilliTS();
1103 else if (!RTStrICmp(pcszSeeder, "system"))
1104 {
1105 RTRAND hRand;
1106 rc = RTRandAdvCreateSystemTruer(&hRand);
1107 if (RT_SUCCESS(rc))
1108 {
1109 RTRandAdvBytes(hRand, &uSeedToUse, sizeof(uSeedToUse));
1110 RTRandAdvDestroy(hRand);
1111 }
1112 }
1113
1114 if (RT_SUCCESS(rc))
1115 rc = VDIoRndCreate(&pGlob->pIoRnd, cbPattern, uSeed);
1116 }
1117
1118 return rc;
1119}
1120
1121static DECLCALLBACK(int) vdScriptHandlerIoRngDestroy(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1122{
1123 if (pGlob->pIoRnd)
1124 {
1125 VDIoRndDestroy(pGlob->pIoRnd);
1126 pGlob->pIoRnd = NULL;
1127 }
1128 else
1129 RTPrintf("WARNING: No I/O RNG active, faulty script. Continuing\n");
1130
1131 return VINF_SUCCESS;
1132}
1133
1134static DECLCALLBACK(int) vdScriptHandlerSleep(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1135{
1136 int rc = VINF_SUCCESS;
1137 uint64_t cMillies = 0;
1138
1139 for (unsigned i = 0; i < cScriptArgs; i++)
1140 {
1141 switch (paScriptArgs[i].chId)
1142 {
1143 case 't':
1144 {
1145 cMillies = paScriptArgs[i].u.u64;
1146 break;
1147 }
1148 default:
1149 AssertMsgFailed(("Invalid argument given!\n"));
1150 }
1151 }
1152
1153 rc = RTThreadSleep(cMillies);
1154 return rc;
1155}
1156
1157static DECLCALLBACK(int) vdScriptHandlerDumpFile(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1158{
1159 int rc = VINF_SUCCESS;
1160 const char *pcszFile = NULL;
1161 const char *pcszPathToDump = NULL;
1162
1163 for (unsigned i = 0; i < cScriptArgs; i++)
1164 {
1165 switch (paScriptArgs[i].chId)
1166 {
1167 case 'f':
1168 {
1169 pcszFile = paScriptArgs[i].u.pcszString;
1170 break;
1171 }
1172 case 'p':
1173 {
1174 pcszPathToDump = paScriptArgs[i].u.pcszString;
1175 break;
1176 }
1177 default:
1178 AssertMsgFailed(("Invalid argument given!\n"));
1179 }
1180 }
1181
1182 /* Check for the file. */
1183 PVDFILE pIt = NULL;
1184 bool fFound = false;
1185 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1186 {
1187 if (!RTStrCmp(pIt->pszName, pcszFile))
1188 {
1189 fFound = true;
1190 break;
1191 }
1192 }
1193
1194 if (fFound)
1195 {
1196 RTPrintf("Dumping memory file %s to %s, this might take some time\n", pcszFile, pcszPathToDump);
1197 rc = VDMemDiskWriteToFile(pIt->pMemDisk, pcszPathToDump);
1198 }
1199 else
1200 rc = VERR_FILE_NOT_FOUND;
1201
1202 return rc;
1203}
1204
1205static DECLCALLBACK(int) vdScriptHandlerCreateDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1206{
1207 int rc = VINF_SUCCESS;
1208 const char *pcszDisk = NULL;
1209 PVDDISK pDisk = NULL;
1210 bool fVerify = false;
1211
1212 for (unsigned i = 0; i < cScriptArgs; i++)
1213 {
1214 switch (paScriptArgs[i].chId)
1215 {
1216 case 'n':
1217 {
1218 pcszDisk = paScriptArgs[i].u.pcszString;
1219 break;
1220 }
1221 case 'v':
1222 {
1223 fVerify = paScriptArgs[i].u.fFlag;
1224 break;
1225 }
1226 default:
1227 AssertMsgFailed(("Invalid argument given!\n"));
1228 }
1229 }
1230
1231 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1232 if (pDisk)
1233 rc = VERR_ALREADY_EXISTS;
1234 else
1235 {
1236 pDisk = (PVDDISK)RTMemAllocZ(sizeof(VDDISK));
1237 if (pDisk)
1238 {
1239 pDisk->pszName = RTStrDup(pcszDisk);
1240 if (pDisk->pszName)
1241 {
1242 rc = VINF_SUCCESS;
1243
1244 if (fVerify)
1245 {
1246 rc = VDMemDiskCreate(&pDisk->pMemDiskVerify, 0 /* Growing */);
1247 if (RT_SUCCESS(rc))
1248 {
1249 rc = RTCritSectInit(&pDisk->CritSectVerify);
1250 if (RT_FAILURE(rc))
1251 VDMemDiskDestroy(pDisk->pMemDiskVerify);
1252 }
1253 }
1254
1255 if (RT_SUCCESS(rc))
1256 {
1257 rc = VDCreate(pGlob->pInterfacesDisk, VDTYPE_HDD, &pDisk->pVD);
1258
1259 if (RT_SUCCESS(rc))
1260 RTListAppend(&pGlob->ListDisks, &pDisk->ListNode);
1261 else
1262 {
1263 if (fVerify)
1264 {
1265 RTCritSectDelete(&pDisk->CritSectVerify);
1266 VDMemDiskDestroy(pDisk->pMemDiskVerify);
1267 }
1268 RTStrFree(pDisk->pszName);
1269 }
1270 }
1271 }
1272 else
1273 rc = VERR_NO_MEMORY;
1274
1275 if (RT_FAILURE(rc))
1276 RTMemFree(pDisk);
1277 }
1278 else
1279 rc = VERR_NO_MEMORY;
1280 }
1281 return rc;
1282}
1283
1284static DECLCALLBACK(int) vdScriptHandlerDestroyDisk(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1285{
1286 int rc = VINF_SUCCESS;
1287 const char *pcszDisk = NULL;
1288 PVDDISK pDisk = NULL;
1289
1290 for (unsigned i = 0; i < cScriptArgs; i++)
1291 {
1292 switch (paScriptArgs[i].chId)
1293 {
1294 case 'n':
1295 {
1296 pcszDisk = paScriptArgs[i].u.pcszString;
1297 break;
1298 }
1299 default:
1300 AssertMsgFailed(("Invalid argument given!\n"));
1301 }
1302 }
1303
1304 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1305 if (pDisk)
1306 {
1307 RTListNodeRemove(&pDisk->ListNode);
1308 VDDestroy(pDisk->pVD);
1309 if (pDisk->pMemDiskVerify)
1310 {
1311 VDMemDiskDestroy(pDisk->pMemDiskVerify);
1312 RTCritSectDelete(&pDisk->CritSectVerify);
1313 }
1314 RTStrFree(pDisk->pszName);
1315 RTMemFree(pDisk);
1316 }
1317 else
1318 rc = VERR_NOT_FOUND;
1319
1320 return rc;
1321}
1322
1323static DECLCALLBACK(int) vdScriptHandlerCompareDisks(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1324{
1325 int rc = VINF_SUCCESS;
1326 const char *pcszDisk1 = NULL;
1327 PVDDISK pDisk1 = NULL;
1328 const char *pcszDisk2 = NULL;
1329 PVDDISK pDisk2 = NULL;
1330
1331 for (unsigned i = 0; i < cScriptArgs; i++)
1332 {
1333 switch (paScriptArgs[i].chId)
1334 {
1335 case '1':
1336 {
1337 pcszDisk1 = paScriptArgs[i].u.pcszString;
1338 break;
1339 }
1340 case '2':
1341 {
1342 pcszDisk2 = paScriptArgs[i].u.pcszString;
1343 break;
1344 }
1345 default:
1346 AssertMsgFailed(("Invalid argument given!\n"));
1347 }
1348 }
1349
1350 pDisk1 = tstVDIoGetDiskByName(pGlob, pcszDisk1);
1351 pDisk2 = tstVDIoGetDiskByName(pGlob, pcszDisk2);
1352
1353 if (pDisk1 && pDisk2)
1354 {
1355 uint8_t *pbBuf1 = (uint8_t *)RTMemAllocZ(16 * _1M);
1356 uint8_t *pbBuf2 = (uint8_t *)RTMemAllocZ(16 * _1M);
1357 if (pbBuf1 && pbBuf2)
1358 {
1359 uint64_t cbDisk1, cbDisk2;
1360 uint64_t uOffCur = 0;
1361
1362 cbDisk1 = VDGetSize(pDisk1->pVD, VD_LAST_IMAGE);
1363 cbDisk2 = VDGetSize(pDisk2->pVD, VD_LAST_IMAGE);
1364
1365 if (cbDisk1 != cbDisk2)
1366 RTPrintf("Disks differ in size %llu vs %llu\n", cbDisk1, cbDisk2);
1367 else
1368 {
1369 while (uOffCur < cbDisk1)
1370 {
1371 size_t cbRead = RT_MIN(cbDisk1, 16 * _1M);
1372
1373 rc = VDRead(pDisk1->pVD, uOffCur, pbBuf1, cbRead);
1374 if (RT_SUCCESS(rc))
1375 rc = VDRead(pDisk2->pVD, uOffCur, pbBuf2, cbRead);
1376
1377 if (RT_SUCCESS(rc))
1378 {
1379 if (memcmp(pbBuf1, pbBuf2, cbRead))
1380 {
1381 RTPrintf("Disks differ at offset %llu\n", uOffCur);
1382 rc = VERR_DEV_IO_ERROR;
1383 break;
1384 }
1385 }
1386 else
1387 {
1388 RTPrintf("Reading one disk at offset %llu failed\n", uOffCur);
1389 break;
1390 }
1391
1392 uOffCur += cbRead;
1393 cbDisk1 -= cbRead;
1394 }
1395 }
1396 RTMemFree(pbBuf1);
1397 RTMemFree(pbBuf2);
1398 }
1399 else
1400 {
1401 if (pbBuf1)
1402 RTMemFree(pbBuf1);
1403 if (pbBuf2)
1404 RTMemFree(pbBuf2);
1405 rc = VERR_NO_MEMORY;
1406 }
1407 }
1408 else
1409 rc = VERR_NOT_FOUND;
1410
1411 return rc;
1412}
1413
1414static DECLCALLBACK(int) vdScriptHandlerDumpDiskInfo(PVDTESTGLOB pGlob, PVDSCRIPTARG paScriptArgs, unsigned cScriptArgs)
1415{
1416 int rc = VINF_SUCCESS;
1417 const char *pcszDisk = NULL;
1418 PVDDISK pDisk = NULL;
1419
1420 for (unsigned i = 0; i < cScriptArgs; i++)
1421 {
1422 switch (paScriptArgs[i].chId)
1423 {
1424 case 'd':
1425 {
1426 pcszDisk = paScriptArgs[i].u.pcszString;
1427 break;
1428 }
1429 default:
1430 AssertMsgFailed(("Invalid argument given!\n"));
1431 }
1432 }
1433
1434 pDisk = tstVDIoGetDiskByName(pGlob, pcszDisk);
1435
1436 if (pDisk)
1437 VDDumpImages(pDisk->pVD);
1438 else
1439 rc = VERR_NOT_FOUND;
1440
1441 return rc;
1442}
1443
1444static DECLCALLBACK(int) tstVDIoFileOpen(void *pvUser, const char *pszLocation,
1445 uint32_t fOpen,
1446 PFNVDCOMPLETED pfnCompleted,
1447 void **ppStorage)
1448{
1449 int rc = VINF_SUCCESS;
1450 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1451 bool fFound = false;
1452
1453 /*
1454 * Some backends use ./ for paths, strip it.
1455 * @todo: Implement proper directory support for the
1456 * memory filesystem.
1457 */
1458 if ( strlen(pszLocation) >= 2
1459 && *pszLocation == '.'
1460 && pszLocation[1] == '/')
1461 pszLocation += 2;
1462
1463 /* Check if the file exists. */
1464 PVDFILE pIt = NULL;
1465 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1466 {
1467 if (!RTStrCmp(pIt->pszName, pszLocation))
1468 {
1469 fFound = true;
1470 break;
1471 }
1472 }
1473
1474 if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_CREATE)
1475 {
1476 /* If the file exists delete the memory disk. */
1477 if (fFound)
1478 rc = VDMemDiskSetSize(pIt->pMemDisk, 0);
1479 else
1480 {
1481 /* Create completey new. */
1482 pIt = (PVDFILE)RTMemAllocZ(sizeof(VDFILE));
1483 if (pIt)
1484 {
1485 pIt->pszName = RTStrDup(pszLocation);
1486
1487 if (pIt->pszName)
1488 {
1489 rc = VDMemDiskCreate(&pIt->pMemDisk, 0);
1490 }
1491 else
1492 rc = VERR_NO_MEMORY;
1493
1494 if (RT_FAILURE(rc))
1495 {
1496 if (pIt->pszName)
1497 RTStrFree(pIt->pszName);
1498 RTMemFree(pIt);
1499 }
1500 }
1501 else
1502 rc = VERR_NO_MEMORY;
1503
1504 RTListAppend(&pGlob->ListFiles, &pIt->Node);
1505 }
1506 }
1507 else if ((fOpen & RTFILE_O_ACTION_MASK) == RTFILE_O_OPEN)
1508 {
1509 if (!fFound)
1510 rc = VERR_FILE_NOT_FOUND;
1511 }
1512 else
1513 rc = VERR_INVALID_PARAMETER;
1514
1515 if (RT_SUCCESS(rc))
1516 {
1517 AssertPtr(pIt);
1518 PVDSTORAGE pStorage = (PVDSTORAGE)RTMemAllocZ(sizeof(VDSTORAGE));
1519 if (!pStorage)
1520 rc = VERR_NO_MEMORY;
1521
1522 pStorage->pFile = pIt;
1523 pStorage->pfnComplete = pfnCompleted;
1524 *ppStorage = pStorage;
1525 }
1526
1527 return rc;
1528}
1529
1530static DECLCALLBACK(int) tstVDIoFileClose(void *pvUser, void *pStorage)
1531{
1532 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
1533
1534 RTMemFree(pIoStorage);
1535 return VINF_SUCCESS;
1536}
1537
1538static DECLCALLBACK(int) tstVDIoFileDelete(void *pvUser, const char *pcszFilename)
1539{
1540 int rc = VINF_SUCCESS;
1541 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1542 bool fFound = false;
1543
1544 /* Check if the file exists. */
1545 PVDFILE pIt = NULL;
1546 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1547 {
1548 if (!RTStrCmp(pIt->pszName, pcszFilename))
1549 {
1550 fFound = true;
1551 break;
1552 }
1553 }
1554
1555 if (fFound)
1556 {
1557 RTListNodeRemove(&pIt->Node);
1558 VDMemDiskDestroy(pIt->pMemDisk);
1559 RTStrFree(pIt->pszName);
1560 RTMemFree(pIt);
1561 }
1562 else
1563 rc = VERR_FILE_NOT_FOUND;
1564
1565 return rc;
1566}
1567
1568static DECLCALLBACK(int) tstVDIoFileMove(void *pvUser, const char *pcszSrc, const char *pcszDst, unsigned fMove)
1569{
1570 int rc = VINF_SUCCESS;
1571 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1572 bool fFound = false;
1573
1574 /* Check if the file exists. */
1575 PVDFILE pIt = NULL;
1576 RTListForEach(&pGlob->ListFiles, pIt, VDFILE, Node)
1577 {
1578 if (!RTStrCmp(pIt->pszName, pcszSrc))
1579 {
1580 fFound = true;
1581 break;
1582 }
1583 }
1584
1585 if (fFound)
1586 {
1587 char *pszNew = RTStrDup(pcszDst);
1588 if (pszNew)
1589 {
1590 RTStrFree(pIt->pszName);
1591 pIt->pszName = pszNew;
1592 }
1593 else
1594 rc = VERR_NO_MEMORY;
1595 }
1596 else
1597 rc = VERR_FILE_NOT_FOUND;
1598
1599 return rc;
1600}
1601
1602static DECLCALLBACK(int) tstVDIoFileGetFreeSpace(void *pvUser, const char *pcszFilename, int64_t *pcbFreeSpace)
1603{
1604 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
1605
1606 *pcbFreeSpace = ~0ULL; /** @todo: Implement */
1607 return VINF_SUCCESS;
1608}
1609
1610static DECLCALLBACK(int) tstVDIoFileGetModificationTime(void *pvUser, const char *pcszFilename, PRTTIMESPEC pModificationTime)
1611{
1612 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
1613
1614 /** @todo: Implement */
1615 return VINF_SUCCESS;
1616}
1617
1618static DECLCALLBACK(int) tstVDIoFileGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
1619{
1620 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
1621
1622 return VDMemDiskGetSize(pIoStorage->pFile->pMemDisk, pcbSize);
1623}
1624
1625static DECLCALLBACK(int) tstVDIoFileSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
1626{
1627 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
1628
1629 return VDMemDiskSetSize(pIoStorage->pFile->pMemDisk, cbSize);
1630}
1631
1632static DECLCALLBACK(int) tstVDIoFileWriteSync(void *pvUser, void *pStorage, uint64_t uOffset,
1633 const void *pvBuffer, size_t cbBuffer, size_t *pcbWritten)
1634{
1635 int rc = VINF_SUCCESS;
1636 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
1637
1638 RTSGBUF SgBuf;
1639 RTSGSEG Seg;
1640
1641 Seg.pvSeg = (void *)pvBuffer;
1642 Seg.cbSeg = cbBuffer;
1643 RTSgBufInit(&SgBuf, &Seg, 1);
1644 rc = VDMemDiskWrite(pIoStorage->pFile->pMemDisk, uOffset, cbBuffer, &SgBuf);
1645 if (RT_SUCCESS(rc) && pcbWritten)
1646 *pcbWritten = cbBuffer;
1647
1648 return rc;
1649}
1650
1651static DECLCALLBACK(int) tstVDIoFileReadSync(void *pvUser, void *pStorage, uint64_t uOffset,
1652 void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
1653{
1654 int rc = VINF_SUCCESS;
1655 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
1656
1657 RTSGBUF SgBuf;
1658 RTSGSEG Seg;
1659
1660 Seg.pvSeg = pvBuffer;
1661 Seg.cbSeg = cbBuffer;
1662 RTSgBufInit(&SgBuf, &Seg, 1);
1663 rc = VDMemDiskRead(pIoStorage->pFile->pMemDisk, uOffset, cbBuffer, &SgBuf);
1664 if (RT_SUCCESS(rc) && pcbRead)
1665 *pcbRead = cbBuffer;
1666
1667 return rc;
1668}
1669
1670static DECLCALLBACK(int) tstVDIoFileFlushSync(void *pvUser, void *pStorage)
1671{
1672 /* nothing to do. */
1673 return VINF_SUCCESS;
1674}
1675
1676static DECLCALLBACK(int) tstVDIoFileReadAsync(void *pvUser, void *pStorage, uint64_t uOffset,
1677 PCRTSGSEG paSegments, size_t cSegments,
1678 size_t cbRead, void *pvCompletion,
1679 void **ppTask)
1680{
1681 int rc = VINF_SUCCESS;
1682 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1683 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
1684
1685 rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pIoStorage->pFile->pMemDisk, VDIOTXDIR_READ, uOffset,
1686 cbRead, paSegments, cSegments, pIoStorage->pfnComplete, pvCompletion);
1687 if (RT_SUCCESS(rc))
1688 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1689
1690 return rc;
1691}
1692
1693static DECLCALLBACK(int) tstVDIoFileWriteAsync(void *pvUser, void *pStorage, uint64_t uOffset,
1694 PCRTSGSEG paSegments, size_t cSegments,
1695 size_t cbWrite, void *pvCompletion,
1696 void **ppTask)
1697{
1698 int rc = VINF_SUCCESS;
1699 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1700 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
1701
1702 rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pIoStorage->pFile->pMemDisk, VDIOTXDIR_WRITE, uOffset,
1703 cbWrite, paSegments, cSegments, pIoStorage->pfnComplete, pvCompletion);
1704 if (RT_SUCCESS(rc))
1705 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1706
1707 return rc;
1708}
1709
1710static DECLCALLBACK(int) tstVDIoFileFlushAsync(void *pvUser, void *pStorage, void *pvCompletion,
1711 void **ppTask)
1712{
1713 int rc = VINF_SUCCESS;
1714 PVDTESTGLOB pGlob = (PVDTESTGLOB)pvUser;
1715 PVDSTORAGE pIoStorage = (PVDSTORAGE)pStorage;
1716
1717 rc = VDIoBackendMemTransfer(pGlob->pIoBackend, pIoStorage->pFile->pMemDisk, VDIOTXDIR_FLUSH, 0,
1718 0, NULL, 0, pIoStorage->pfnComplete, pvCompletion);
1719 if (RT_SUCCESS(rc))
1720 rc = VERR_VD_ASYNC_IO_IN_PROGRESS;
1721
1722 return rc;
1723}
1724
1725static int tstVDIoTestInit(PVDIOTEST pIoTest, PVDTESTGLOB pGlob, bool fRandomAcc, uint64_t cbIo,
1726 size_t cbBlkSize, uint64_t offStart, uint64_t offEnd,
1727 unsigned uWriteChance)
1728{
1729 int rc = VINF_SUCCESS;
1730
1731 pIoTest->fRandomAccess = fRandomAcc;
1732 pIoTest->cbIo = cbIo;
1733 pIoTest->cbBlkIo = cbBlkSize;
1734 pIoTest->offStart = offStart;
1735 pIoTest->offEnd = offEnd;
1736 pIoTest->uWriteChance = uWriteChance;
1737 pIoTest->pIoRnd = pGlob->pIoRnd;
1738
1739 if (fRandomAcc)
1740 {
1741 uint64_t cbRange = pIoTest->offEnd < pIoTest->offStart
1742 ? pIoTest->offStart - pIoTest->offEnd
1743 : pIoTest->offEnd - pIoTest->offStart;
1744
1745 pIoTest->u.Rnd.cBlocks = cbRange / cbBlkSize + ((cbRange % cbBlkSize) ? 1 : 0);
1746 pIoTest->u.Rnd.cBlocksLeft = pIoTest->u.Rnd.cBlocks;
1747 pIoTest->u.Rnd.pbMapAccessed = (uint8_t *)RTMemAllocZ(pIoTest->u.Rnd.cBlocks / 8
1748 + ((pIoTest->u.Rnd.cBlocks % 8)
1749 ? 1
1750 : 0));
1751 if (!pIoTest->u.Rnd.pbMapAccessed)
1752 rc = VERR_NO_MEMORY;
1753 }
1754 else
1755 pIoTest->u.offNext = pIoTest->offEnd < pIoTest->offStart ? pIoTest->offStart - cbBlkSize : 0;
1756
1757 return rc;
1758}
1759
1760static void tstVDIoTestDestroy(PVDIOTEST pIoTest)
1761{
1762 if (pIoTest->fRandomAccess)
1763 RTMemFree(pIoTest->u.Rnd.pbMapAccessed);
1764}
1765
1766static bool tstVDIoTestRunning(PVDIOTEST pIoTest)
1767{
1768 return pIoTest->cbIo > 0;
1769}
1770
1771static bool tstVDIoTestReqOutstanding(PVDIOREQ pIoReq)
1772{
1773 return pIoReq->fOutstanding;
1774}
1775
1776/**
1777 * Returns true with the given chance in percent.
1778 *
1779 * @returns true or false
1780 * @param iPercentage The percentage of the chance to return true.
1781 */
1782static bool tstVDIoTestIsTrue(PVDIOTEST pIoTest, int iPercentage)
1783{
1784 int uRnd = VDIoRndGetU32Ex(pIoTest->pIoRnd, 0, 100);
1785
1786 return (uRnd <= iPercentage); /* This should be enough for our purpose */
1787}
1788
1789static int tstVDIoTestReqInit(PVDIOTEST pIoTest, PVDIOREQ pIoReq, void *pvUser)
1790{
1791 int rc = VINF_SUCCESS;
1792
1793 if (pIoTest->cbIo)
1794 {
1795 /* Read or Write? */
1796 pIoReq->enmTxDir = tstVDIoTestIsTrue(pIoTest, pIoTest->uWriteChance) ? VDIOREQTXDIR_WRITE : VDIOREQTXDIR_READ;
1797 pIoReq->cbReq = RT_MIN(pIoTest->cbBlkIo, pIoTest->cbIo);
1798 pIoTest->cbIo -= pIoReq->cbReq;
1799 pIoReq->DataSeg.cbSeg = pIoReq->cbReq;
1800
1801 if (pIoReq->enmTxDir == VDIOREQTXDIR_WRITE)
1802 {
1803 rc = VDIoRndGetBuffer(pIoTest->pIoRnd, &pIoReq->DataSeg.pvSeg, pIoReq->cbReq);
1804 AssertRC(rc);
1805 }
1806 else
1807 {
1808 /* Read */
1809 pIoReq->DataSeg.pvSeg = pIoReq->pvBufRead;
1810 }
1811
1812 if (RT_SUCCESS(rc))
1813 {
1814 RTSgBufInit(&pIoReq->SgBuf, &pIoReq->DataSeg, 1);
1815
1816 if (pIoTest->fRandomAccess)
1817 {
1818 int idx = -1;
1819
1820 idx = ASMBitFirstClear(pIoTest->u.Rnd.pbMapAccessed, pIoTest->u.Rnd.cBlocks);
1821
1822 /* In case this is the last request we don't need to search further. */
1823 if (pIoTest->u.Rnd.cBlocksLeft > 1)
1824 {
1825 int idxIo;
1826 idxIo = VDIoRndGetU32Ex(pIoTest->pIoRnd, idx, pIoTest->u.Rnd.cBlocks - 1);
1827
1828 /*
1829 * If the bit is marked free use it, otherwise search for the next free bit
1830 * and if that doesn't work use the first free bit.
1831 */
1832 if (ASMBitTest(pIoTest->u.Rnd.pbMapAccessed, idxIo))
1833 {
1834 idxIo = ASMBitNextClear(pIoTest->u.Rnd.pbMapAccessed, pIoTest->u.Rnd.cBlocks, idxIo);
1835 if (idxIo != -1)
1836 idx = idxIo;
1837 }
1838 else
1839 idx = idxIo;
1840 }
1841
1842 Assert(idx != -1);
1843 pIoReq->off = idx * pIoTest->cbBlkIo;
1844 pIoTest->u.Rnd.cBlocksLeft--;
1845 if (!pIoTest->u.Rnd.cBlocksLeft)
1846 {
1847 /* New round, clear everything. */
1848 ASMBitClearRange(pIoTest->u.Rnd.pbMapAccessed, 0, pIoTest->u.Rnd.cBlocks);
1849 pIoTest->u.Rnd.cBlocksLeft = pIoTest->u.Rnd.cBlocks;
1850 }
1851 else
1852 ASMBitSet(pIoTest->u.Rnd.pbMapAccessed, idx);
1853 }
1854 else
1855 {
1856 pIoReq->off = pIoTest->u.offNext;
1857 if (pIoTest->offEnd < pIoTest->offStart)
1858 {
1859 pIoTest->u.offNext = pIoTest->u.offNext == 0
1860 ? pIoTest->offEnd - pIoTest->cbBlkIo
1861 : RT_MAX(pIoTest->offEnd, pIoTest->u.offNext - pIoTest->cbBlkIo);
1862 }
1863 else
1864 {
1865 pIoTest->u.offNext = pIoTest->u.offNext + pIoTest->cbBlkIo >= pIoTest->offEnd
1866 ? 0
1867 : RT_MIN(pIoTest->offEnd, pIoTest->u.offNext + pIoTest->cbBlkIo);
1868 }
1869 }
1870 pIoReq->pvUser = pvUser;
1871 pIoReq->fOutstanding = true;
1872 }
1873 }
1874 else
1875 rc = VERR_ACCESS_DENIED;
1876
1877 return rc;
1878}
1879
1880static void tstVDIoTestReqComplete(void *pvUser1, void *pvUser2, int rcReq)
1881{
1882 PVDIOREQ pIoReq = (PVDIOREQ)pvUser1;
1883 RTSEMEVENT hEventSem = (RTSEMEVENT)pvUser2;
1884 PVDDISK pDisk = (PVDDISK)pIoReq->pvUser;
1885
1886 if (pDisk->pMemDiskVerify)
1887 {
1888 switch (pIoReq->enmTxDir)
1889 {
1890 case VDIOREQTXDIR_READ:
1891 {
1892 RTCritSectEnter(&pDisk->CritSectVerify);
1893 RTSgBufReset(&pIoReq->SgBuf);
1894
1895 if (VDMemDiskCmp(pDisk->pMemDiskVerify, pIoReq->off, pIoReq->cbReq,
1896 &pIoReq->SgBuf))
1897 RTPrintf("Corrupted disk at offset %llu!\n", pIoReq->off);
1898 RTCritSectLeave(&pDisk->CritSectVerify);
1899 }
1900 case VDIOREQTXDIR_WRITE:
1901 {
1902 RTCritSectEnter(&pDisk->CritSectVerify);
1903 RTSgBufReset(&pIoReq->SgBuf);
1904
1905 int rc = VDMemDiskWrite(pDisk->pMemDiskVerify, pIoReq->off, pIoReq->cbReq,
1906 &pIoReq->SgBuf);
1907 AssertRC(rc);
1908 RTCritSectLeave(&pDisk->CritSectVerify);
1909 break;
1910 }
1911 case VDIOREQTXDIR_FLUSH:
1912 break;
1913 }
1914 }
1915
1916 ASMAtomicXchgBool(&pIoReq->fOutstanding, false);
1917 RTSemEventSignal(hEventSem);
1918 return;
1919}
1920
1921/**
1922 * Returns the disk handle by name or NULL if not found
1923 *
1924 * @returns Disk handle or NULL if the disk could not be found.
1925 *
1926 * @param pGlob Global test state.
1927 * @param pcszDisk Name of the disk to get.
1928 */
1929static PVDDISK tstVDIoGetDiskByName(PVDTESTGLOB pGlob, const char *pcszDisk)
1930{
1931 PVDDISK pIt = NULL;
1932 bool fFound = false;
1933
1934 LogFlowFunc(("pGlob=%#p pcszDisk=%s\n", pGlob, pcszDisk));
1935
1936 RTListForEach(&pGlob->ListDisks, pIt, VDDISK, ListNode)
1937 {
1938 if (!RTStrCmp(pIt->pszName, pcszDisk))
1939 {
1940 fFound = true;
1941 break;
1942 }
1943 }
1944
1945 LogFlowFunc(("return %#p\n", fFound ? pIt : NULL));
1946 return fFound ? pIt : NULL;
1947}
1948
1949/**
1950 * Skips the characters until the given character is reached.
1951 *
1952 * @returns Start of the string with the given character
1953 * or NULL if the string ended before.
1954 *
1955 * @param psz The string to skip.
1956 * @param ch The character.
1957 */
1958static char *tstVDIoScriptSkipUntil(char *psz, char ch)
1959{
1960 while ( *psz != '\0'
1961 && *psz != ch)
1962 psz++;
1963
1964 return psz;
1965}
1966
1967/**
1968 * Skips the spaces of the current string.
1969 *
1970 * @returns Start of the string with a non space character
1971 * or NULL if the string ended before.
1972 *
1973 * @param psz The string to skip.
1974 */
1975static char *tstVDIoScriptSkipSpace(char *psz)
1976{
1977 while ( *psz != '\0'
1978 && RT_C_IS_SPACE(*psz))
1979 psz++;
1980
1981 return psz;
1982}
1983
1984/**
1985 * Skips all characters until a space is reached of the current
1986 * string.
1987 *
1988 * @returns Start of the string with a space character
1989 * or NULL if the string ended before.
1990 *
1991 * @param psz The string to skip.
1992 */
1993static char *tstVDIoScriptSkipNonSpace(char *psz)
1994{
1995 while ( *psz != '\0'
1996 && !RT_C_IS_SPACE(*psz))
1997 psz++;
1998
1999 return psz;
2000}
2001
2002/**
2003 * Returns true if the first character of the given string
2004 * contains a character marking a line end (comment or \0
2005 * terminator).
2006 *
2007 * @returns true if the line contains no more characters of
2008 * interest and false otherwise.
2009 *
2010 * @param psz The string to check for.
2011 */
2012static bool tstVDIoIsLineEnd(const char *psz)
2013{
2014 return *psz == '\0' || *psz == '#';
2015}
2016
2017/**
2018 * Parses one argument name, value pair.
2019 *
2020 * @returns IPRT status code.
2021 *
2022 * @param pVDScriptAction Script action.
2023 * @param pcszName Argument name.
2024 * @param pcszValue Argument value.
2025 * @param pScriptArg Where to fill in the parsed
2026 * argument.
2027 * @param pfMandatory Where to store whether the argument
2028 * is mandatory.
2029 */
2030static int tstVDIoScriptArgumentParse(PCVDSCRIPTACTION pVDScriptAction, const char *pcszName,
2031 const char *pcszValue, PVDSCRIPTARG pScriptArg, bool *pfMandatory)
2032{
2033 int rc = VERR_NOT_FOUND;
2034
2035 for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++)
2036 {
2037 if (!RTStrCmp(pVDScriptAction->paArgDesc[i].pcszName, pcszName))
2038 {
2039 rc = VINF_SUCCESS;
2040
2041 switch (pVDScriptAction->paArgDesc[i].enmType)
2042 {
2043 case VDSCRIPTARGTYPE_BOOL:
2044 {
2045 pScriptArg->enmType = VDSCRIPTARGTYPE_BOOL;
2046 if (!RTStrICmp(pcszValue, "yes") || !RTStrICmp(pcszValue, "on"))
2047 pScriptArg->u.fFlag = true;
2048 else if (!RTStrICmp(pcszValue, "no") || !RTStrICmp(pcszValue, "off"))
2049 pScriptArg->u.fFlag = false;
2050 else
2051 {
2052 RTPrintf("Boolean argument malformed '%s'\n", pcszValue);
2053 rc = VERR_INVALID_PARAMETER;
2054 }
2055 break;
2056 }
2057 case VDSCRIPTARGTYPE_SIGNED_NUMBER:
2058 {
2059 pScriptArg->enmType = VDSCRIPTARGTYPE_SIGNED_NUMBER;
2060 AssertMsgFailed(("todo\n"));
2061 break;
2062 }
2063 case VDSCRIPTARGTYPE_STRING:
2064 {
2065 pScriptArg->enmType = VDSCRIPTARGTYPE_STRING;
2066 pScriptArg->u.pcszString = pcszValue;
2067 break;
2068 }
2069 case VDSCRIPTARGTYPE_UNSIGNED_NUMBER:
2070 {
2071 char *pszSuffix = NULL;
2072
2073 pScriptArg->enmType = VDSCRIPTARGTYPE_UNSIGNED_NUMBER;
2074 rc = RTStrToUInt64Ex(pcszValue, &pszSuffix, 10, &pScriptArg->u.u64);
2075 if (rc == VWRN_TRAILING_CHARS)
2076 {
2077 switch (*pszSuffix)
2078 {
2079 case 'k':
2080 case 'K':
2081 {
2082 pScriptArg->u.u64 *= _1K;
2083 break;
2084 }
2085 case 'm':
2086 case 'M':
2087 {
2088 pScriptArg->u.u64 *= _1M;
2089 break;
2090 }
2091 case 'g':
2092 case 'G':
2093 {
2094 pScriptArg->u.u64 *= _1G;
2095 break;
2096 }
2097 default:
2098 {
2099 RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
2100 rc = VERR_INVALID_PARAMETER;
2101 }
2102 }
2103 if (rc != VERR_INVALID_PARAMETER)
2104 rc = VINF_SUCCESS;
2105 }
2106
2107 break;
2108 }
2109 case VDSCRIPTARGTYPE_UNSIGNED_RANGE:
2110 {
2111 char *pszSuffix = NULL;
2112
2113 pScriptArg->enmType = VDSCRIPTARGTYPE_UNSIGNED_RANGE;
2114 rc = RTStrToUInt64Ex(pcszValue, &pszSuffix, 10, &pScriptArg->u.Range.Start);
2115 if (rc == VWRN_TRAILING_CHARS)
2116 {
2117 if (*pszSuffix != '-')
2118 {
2119 switch (*pszSuffix)
2120 {
2121 case 'k':
2122 case 'K':
2123 {
2124 pScriptArg->u.u64 *= _1K;
2125 break;
2126 }
2127 case 'm':
2128 case 'M':
2129 {
2130 pScriptArg->u.u64 *= _1M;
2131 break;
2132 }
2133 case 'g':
2134 case 'G':
2135 {
2136 pScriptArg->u.u64 *= _1G;
2137 break;
2138 }
2139 default:
2140 {
2141 RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
2142 rc = VERR_INVALID_PARAMETER;
2143 }
2144 }
2145 if (RT_SUCCESS(rc))
2146 pszSuffix++;
2147 }
2148
2149 if (*pszSuffix == '-')
2150 {
2151 pszSuffix++;
2152 rc = RTStrToUInt64Ex(pszSuffix, &pszSuffix, 10, &pScriptArg->u.Range.End);
2153 if (rc == VWRN_TRAILING_CHARS)
2154 {
2155 switch (*pszSuffix)
2156 {
2157 case 'k':
2158 case 'K':
2159 {
2160 pScriptArg->u.Range.End *= _1K;
2161 break;
2162 }
2163 case 'm':
2164 case 'M':
2165 {
2166 pScriptArg->u.Range.End *= _1M;
2167 break;
2168 }
2169 case 'g':
2170 case 'G':
2171 {
2172 pScriptArg->u.Range.End *= _1G;
2173 break;
2174 }
2175 default:
2176 {
2177 RTPrintf("Invalid size suffix '%s'\n", pszSuffix);
2178 rc = VERR_INVALID_PARAMETER;
2179 }
2180 }
2181 }
2182 }
2183 else
2184 rc = VERR_INVALID_PARAMETER;
2185 }
2186 else
2187 rc = VERR_INVALID_PARAMETER;
2188
2189 if (rc == VERR_INVALID_PARAMETER)
2190 RTPrintf("Invalid range format\n");
2191 break;
2192 }
2193 default:
2194 AssertMsgFailed(("Invalid script argument type\n"));
2195 }
2196
2197 if (RT_SUCCESS(rc))
2198 {
2199 pScriptArg->chId = pVDScriptAction->paArgDesc[i].chId;
2200 *pfMandatory = !!(pVDScriptAction->paArgDesc[i].fFlags & VDSCRIPTARGDESC_FLAG_MANDATORY);
2201 }
2202 break;
2203 }
2204 }
2205
2206 if (rc == VERR_NOT_FOUND)
2207 RTPrintf("Argument '%s' not found\n", pcszName);
2208
2209 return rc;
2210}
2211
2212/**
2213 * Parses the arguments of a action in the script.
2214 *
2215 * @returns IPRT status code.
2216 *
2217 * @param psz Argument string.
2218 * @param pVDScriptAction The script action to parses
2219 * arguments for.
2220 * @param paScriptArgs Where to store the arguments.
2221 * @param pcScriptArgs Where to store the actual number of
2222 * arguments parsed.
2223 */
2224static int tstVDIoScriptArgumentListParse(char *psz, PCVDSCRIPTACTION pVDScriptAction, PVDSCRIPTARG paScriptArgs, unsigned *pcScriptArgs)
2225{
2226 int rc = VINF_SUCCESS;
2227 unsigned cMandatoryArgsReq = 0;
2228 unsigned cScriptArgs = 0;
2229
2230 /* Count the number of mandatory arguments first. */
2231 for (unsigned i = 0; i < pVDScriptAction->cArgDescs; i++)
2232 if (pVDScriptAction->paArgDesc[i].fFlags & VDSCRIPTARGDESC_FLAG_MANDATORY)
2233 cMandatoryArgsReq++;
2234
2235 /* One argument is given in the form name=value. */
2236 *pcScriptArgs = 0;
2237
2238 while ( psz
2239 && !tstVDIoIsLineEnd(psz))
2240 {
2241 const char *pcszName = psz;
2242
2243 psz = tstVDIoScriptSkipUntil(psz, '=');
2244 if (!tstVDIoIsLineEnd(psz))
2245 {
2246 *psz = '\0'; /* Overwrite */
2247 psz++;
2248 const char *pcszValue = psz;
2249
2250 psz = tstVDIoScriptSkipNonSpace(psz);
2251 if (!tstVDIoIsLineEnd(psz))
2252 {
2253 *psz = '\0'; /* Overwrite */
2254 psz++;
2255 psz = tstVDIoScriptSkipSpace(psz);
2256 }
2257
2258 pcszValue = tstVDIoScriptSkipSpace((char *)pcszValue);
2259 if (*pcszValue == '\0')
2260 {
2261 RTPrintf("Value missing for argument '%s'\n", pcszName);
2262 rc = VERR_INVALID_STATE;
2263 break;
2264 }
2265
2266 /* We have the name and value pair now. */
2267 bool fMandatory = false; /* Shut up gcc */
2268 rc = tstVDIoScriptArgumentParse(pVDScriptAction, pcszName, pcszValue, &paScriptArgs[cScriptArgs], &fMandatory);
2269 if (RT_SUCCESS(rc))
2270 {
2271 if (fMandatory)
2272 cMandatoryArgsReq--;
2273 cScriptArgs++;
2274 }
2275 }
2276 else
2277 {
2278 RTPrintf("Argument in invalid form\n");
2279 rc = VERR_INVALID_STATE;
2280 break;
2281 }
2282 }
2283
2284 if ( RT_SUCCESS(rc)
2285 && cMandatoryArgsReq)
2286 {
2287 /* No arguments anymore but there are still mandatory arguments left. */
2288 RTPrintf("There are %u arguments missing for script action '%s\n", pVDScriptAction->pcszAction);
2289 rc = VERR_INVALID_STATE;
2290 }
2291
2292 if (RT_SUCCESS(rc))
2293 *pcScriptArgs = cScriptArgs;
2294
2295 return rc;
2296}
2297
2298/**
2299 * Executes the script pointed to by the given stream.
2300 *
2301 * @returns IPRT status code.
2302 *
2303 * @param pStrm The stream handle of the script.
2304 * @param pGlob Global test data.
2305 */
2306static int tstVDIoScriptExecute(PRTSTREAM pStrm, PVDTESTGLOB pGlob)
2307{
2308 int rc = VINF_SUCCESS;
2309 char abBuffer[0x1000]; /* Current assumption that a line is never longer than 4096 bytes. */
2310 PVDSCRIPTARG paScriptArgs = NULL;
2311 unsigned cScriptArgsMax = 0;
2312
2313 do
2314 {
2315 memset(abBuffer, 0, sizeof(abBuffer));
2316 rc = RTStrmGetLine(pStrm, abBuffer, sizeof(abBuffer));
2317 if (RT_SUCCESS(rc))
2318 {
2319 const char *pcszAction = NULL;
2320 char *psz = abBuffer;
2321
2322 /* Skip space */
2323 psz = tstVDIoScriptSkipSpace(psz);
2324 if (!tstVDIoIsLineEnd(psz))
2325 {
2326 PCVDSCRIPTACTION pVDScriptAction = NULL;
2327
2328 /* Get the action name. */
2329 pcszAction = psz;
2330
2331 psz = tstVDIoScriptSkipNonSpace(psz);
2332 if (!tstVDIoIsLineEnd(psz))
2333 {
2334 Assert(RT_C_IS_SPACE(*psz));
2335 *psz++ = '\0';
2336 }
2337
2338 /* Find the action. */
2339 for (unsigned i = 0; i < g_cScriptActions; i++)
2340 {
2341 if (!RTStrCmp(pcszAction, g_aScriptActions[i].pcszAction))
2342 {
2343 pVDScriptAction = &g_aScriptActions[i];
2344 break;
2345 }
2346 }
2347
2348 if (pVDScriptAction)
2349 {
2350 /* Parse arguments. */
2351 if (cScriptArgsMax < pVDScriptAction->cArgDescs)
2352 {
2353 /* Increase arguments array. */
2354 if (paScriptArgs)
2355 RTMemFree(paScriptArgs);
2356
2357 cScriptArgsMax = pVDScriptAction->cArgDescs;
2358 paScriptArgs = (PVDSCRIPTARG)RTMemAllocZ(cScriptArgsMax * sizeof(VDSCRIPTARG));
2359 }
2360
2361 if (paScriptArgs)
2362 {
2363 unsigned cScriptArgs;
2364
2365 rc = tstVDIoScriptArgumentListParse(psz, pVDScriptAction, paScriptArgs, &cScriptArgs);
2366 if (RT_SUCCESS(rc))
2367 {
2368 /* Execute the handler. */
2369 rc = pVDScriptAction->pfnHandler(pGlob, paScriptArgs, cScriptArgs);
2370 }
2371 }
2372 else
2373 {
2374 RTPrintf("Out of memory while allocating argument array for script action %s\n", pcszAction);
2375 rc = VERR_NO_MEMORY;
2376 }
2377 }
2378 else
2379 {
2380 RTPrintf("Script action %s is not known\n", pcszAction);
2381 rc = VERR_NOT_FOUND;
2382 }
2383 }
2384 /* else empty line, just continue */
2385 }
2386 } while(RT_SUCCESS(rc));
2387
2388 if (rc == VERR_EOF)
2389 {
2390 RTPrintf("Successfully executed I/O script\n");
2391 rc = VINF_SUCCESS;
2392 }
2393 return rc;
2394}
2395
2396/**
2397 * Executes the given I/O script.
2398 *
2399 * @returns nothing.
2400 *
2401 * @param pcszFilename The script to execute.
2402 */
2403static void tstVDIoScriptRun(const char *pcszFilename)
2404{
2405 int rc = VINF_SUCCESS;
2406 PRTSTREAM pScriptStrm; /**< Stream of the script file. */
2407 VDTESTGLOB GlobTest; /**< Global test data. */
2408
2409 memset(&GlobTest, 0, sizeof(VDTESTGLOB));
2410 RTListInit(&GlobTest.ListFiles);
2411 RTListInit(&GlobTest.ListDisks);
2412
2413 rc = RTStrmOpen(pcszFilename, "r", &pScriptStrm);
2414 if (RT_SUCCESS(rc))
2415 {
2416 /* Init global test data. */
2417 GlobTest.VDIErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
2418 GlobTest.VDIErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
2419 GlobTest.VDIErrorCallbacks.pfnError = tstVDError;
2420 GlobTest.VDIErrorCallbacks.pfnMessage = tstVDMessage;
2421
2422 rc = VDInterfaceAdd(&GlobTest.VDIError, "tstVDIo_VDIError", VDINTERFACETYPE_ERROR,
2423 &GlobTest.VDIErrorCallbacks, NULL, &GlobTest.pInterfacesDisk);
2424 AssertRC(rc);
2425
2426 GlobTest.VDIIoCallbacks.cbSize = sizeof(VDINTERFACEIO);
2427 GlobTest.VDIIoCallbacks.enmInterface = VDINTERFACETYPE_IO;
2428 GlobTest.VDIIoCallbacks.pfnOpen = tstVDIoFileOpen;
2429 GlobTest.VDIIoCallbacks.pfnClose = tstVDIoFileClose;
2430 GlobTest.VDIIoCallbacks.pfnDelete = tstVDIoFileDelete;
2431 GlobTest.VDIIoCallbacks.pfnMove = tstVDIoFileMove;
2432 GlobTest.VDIIoCallbacks.pfnGetFreeSpace = tstVDIoFileGetFreeSpace;
2433 GlobTest.VDIIoCallbacks.pfnGetModificationTime = tstVDIoFileGetModificationTime;
2434 GlobTest.VDIIoCallbacks.pfnGetSize = tstVDIoFileGetSize;
2435 GlobTest.VDIIoCallbacks.pfnSetSize = tstVDIoFileSetSize;
2436 GlobTest.VDIIoCallbacks.pfnWriteSync = tstVDIoFileWriteSync;
2437 GlobTest.VDIIoCallbacks.pfnReadSync = tstVDIoFileReadSync;
2438 GlobTest.VDIIoCallbacks.pfnFlushSync = tstVDIoFileFlushSync;
2439 GlobTest.VDIIoCallbacks.pfnReadAsync = tstVDIoFileReadAsync;
2440 GlobTest.VDIIoCallbacks.pfnWriteAsync = tstVDIoFileWriteAsync;
2441 GlobTest.VDIIoCallbacks.pfnFlushAsync = tstVDIoFileFlushAsync;
2442
2443 rc = VDInterfaceAdd(&GlobTest.VDIIo, "tstVDIo_VDIIo", VDINTERFACETYPE_IO,
2444 &GlobTest.VDIIoCallbacks, &GlobTest, &GlobTest.pInterfacesImages);
2445 AssertRC(rc);
2446
2447 /* Init I/O backend. */
2448 rc = VDIoBackendMemCreate(&GlobTest.pIoBackend);
2449 if (RT_SUCCESS(rc))
2450 {
2451 /* Execute the script. */
2452 rc = tstVDIoScriptExecute(pScriptStrm, &GlobTest);
2453 if (RT_FAILURE(rc))
2454 {
2455 RTPrintf("Executing the script stream failed rc=%Rrc\n", rc);
2456 }
2457 VDIoBackendMemDestroy(GlobTest.pIoBackend);
2458 }
2459 else
2460 RTPrintf("Creating the I/O backend failed rc=%Rrc\n");
2461
2462 RTStrmClose(pScriptStrm);
2463 }
2464 else
2465 RTPrintf("Opening script failed rc=%Rrc\n", rc);
2466}
2467
2468/**
2469 * Shows help message.
2470 */
2471static void printUsage(void)
2472{
2473 RTPrintf("Usage:\n"
2474 "--script <filename> Script to execute\n"
2475 "--replay <filename> Log to replay (not implemented yet)\n");
2476}
2477
2478static const RTGETOPTDEF g_aOptions[] =
2479{
2480 { "--script", 's', RTGETOPT_REQ_STRING },
2481 { "--replay", 'r', RTGETOPT_REQ_STRING },
2482};
2483
2484int main(int argc, char *argv[])
2485{
2486 RTR3Init();
2487 int rc;
2488 RTGETOPTUNION ValueUnion;
2489 RTGETOPTSTATE GetState;
2490 char c;
2491
2492 if (argc != 3)
2493 {
2494 printUsage();
2495 return RTEXITCODE_FAILURE;
2496 }
2497
2498 rc = VDInit();
2499 if (RT_FAILURE(rc))
2500 return RTEXITCODE_FAILURE;
2501
2502 RTGetOptInit(&GetState, argc, argv, g_aOptions,
2503 RT_ELEMENTS(g_aOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
2504
2505 while ( RT_SUCCESS(rc)
2506 && (c = RTGetOpt(&GetState, &ValueUnion)))
2507 {
2508 switch (c)
2509 {
2510 case 's':
2511 tstVDIoScriptRun(ValueUnion.psz);
2512 break;
2513 case 'r':
2514 RTPrintf("Replaying I/O logs is not implemented yet\n");
2515 break;
2516 default:
2517 printUsage();
2518 }
2519 }
2520
2521 rc = VDShutdown();
2522 if (RT_FAILURE(rc))
2523 RTPrintf("tstVDIo: unloading backends failed! rc=%Rrc\n", rc);
2524
2525 return RTEXITCODE_SUCCESS;
2526}
2527
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette