VirtualBox

source: vbox/trunk/src/VBox/Storage/testcase/vbox-img.cpp@ 57087

Last change on this file since 57087 was 55974, checked in by vboxsync, 10 years ago

vbox-img: Add resize command to increase the size of a disk image

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 58.9 KB
Line 
1/* $Id: vbox-img.cpp 55974 2015-05-20 15:57:56Z vboxsync $ */
2/** @file
3 * Standalone image manipulation tool
4 */
5
6/*
7 * Copyright (C) 2010-2014 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
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#include <VBox/vd.h>
22#include <VBox/err.h>
23#include <VBox/version.h>
24#include <iprt/initterm.h>
25#include <iprt/buildconfig.h>
26#include <iprt/path.h>
27#include <iprt/string.h>
28#include <iprt/uuid.h>
29#include <iprt/stream.h>
30#include <iprt/message.h>
31#include <iprt/getopt.h>
32#include <iprt/assert.h>
33#include <iprt/dvm.h>
34#include <iprt/filesystem.h>
35#include <iprt/vfs.h>
36
37static const char *g_pszProgName = "";
38static void printUsage(PRTSTREAM pStrm)
39{
40 RTStrmPrintf(pStrm,
41 "Usage: %s\n"
42 " setuuid --filename <filename>\n"
43 " [--format VDI|VMDK|VHD|...]\n"
44 " [--uuid <uuid>]\n"
45 " [--parentuuid <uuid>]\n"
46 " [--zeroparentuuid]\n"
47 "\n"
48 " geometry --filename <filename>\n"
49 " [--format VDI|VMDK|VHD|...]\n"
50 " [--clearchs]\n"
51 " [--cylinders <number>]\n"
52 " [--heads <number>]\n"
53 " [--sectors <number>]\n"
54 "\n"
55 " convert --srcfilename <filename>\n"
56 " --dstfilename <filename>\n"
57 " [--stdin]|[--stdout]\n"
58 " [--srcformat VDI|VMDK|VHD|RAW|..]\n"
59 " [--dstformat VDI|VMDK|VHD|RAW|..]\n"
60 " [--variant Standard,Fixed,Split2G,Stream,ESX]\n"
61 "\n"
62 " info --filename <filename>\n"
63 "\n"
64 " compact --filename <filename>\n"
65 " [--filesystemaware]\n"
66 "\n"
67 " createcache --filename <filename>\n"
68 " --size <cache size>\n"
69 "\n"
70 " createbase --filename <filename>\n"
71 " --size <size in bytes>\n"
72 " [--format VDI|VMDK|VHD] (default: VDI)\n"
73 " [--variant Standard,Fixed,Split2G,Stream,ESX]\n"
74 " [--dataalignment <alignment in bytes>]\n"
75 "\n"
76 " repair --filename <filename>\n"
77 " [--dry-run]\n"
78 " [--format VDI|VMDK|VHD] (default: autodetect)\n"
79 "\n"
80 " clearcomment --filename <filename>\n"
81 "\n"
82 " resize --filename <filename>\n"
83 " --size <new size>\n",
84 g_pszProgName);
85}
86
87static void showLogo(PRTSTREAM pStrm)
88{
89 static bool s_fShown; /* show only once */
90
91 if (!s_fShown)
92 {
93 RTStrmPrintf(pStrm, VBOX_PRODUCT " Disk Utility " VBOX_VERSION_STRING "\n"
94 "(C) 2005-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
95 "All rights reserved.\n"
96 "\n");
97 s_fShown = true;
98 }
99}
100
101/** command handler argument */
102struct HandlerArg
103{
104 int argc;
105 char **argv;
106};
107
108static PVDINTERFACE pVDIfs;
109
110static DECLCALLBACK(void) handleVDError(void *pvUser, int rc, RT_SRC_POS_DECL,
111 const char *pszFormat, va_list va)
112{
113 NOREF(pvUser);
114 NOREF(rc);
115 RTMsgErrorV(pszFormat, va);
116}
117
118static int handleVDMessage(void *pvUser, const char *pszFormat, va_list va)
119{
120 NOREF(pvUser);
121 RTPrintfV(pszFormat, va);
122 return VINF_SUCCESS;
123}
124
125/**
126 * Print a usage synopsis and the syntax error message.
127 */
128static int errorSyntax(const char *pszFormat, ...)
129{
130 va_list args;
131 showLogo(g_pStdErr); // show logo even if suppressed
132 va_start(args, pszFormat);
133 RTStrmPrintf(g_pStdErr, "\nSyntax error: %N\n", pszFormat, &args);
134 va_end(args);
135 printUsage(g_pStdErr);
136 return 1;
137}
138
139static int errorRuntime(const char *pszFormat, ...)
140{
141 va_list args;
142
143 va_start(args, pszFormat);
144 RTMsgErrorV(pszFormat, args);
145 va_end(args);
146 return 1;
147}
148
149static int parseDiskVariant(const char *psz, unsigned *puImageFlags)
150{
151 int rc = VINF_SUCCESS;
152 unsigned uImageFlags = *puImageFlags;
153
154 while (psz && *psz && RT_SUCCESS(rc))
155 {
156 size_t len;
157 const char *pszComma = strchr(psz, ',');
158 if (pszComma)
159 len = pszComma - psz;
160 else
161 len = strlen(psz);
162 if (len > 0)
163 {
164 /*
165 * Parsing is intentionally inconsistent: "standard" resets the
166 * variant, whereas the other flags are cumulative.
167 */
168 if (!RTStrNICmp(psz, "standard", len))
169 uImageFlags = VD_IMAGE_FLAGS_NONE;
170 else if ( !RTStrNICmp(psz, "fixed", len)
171 || !RTStrNICmp(psz, "static", len))
172 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
173 else if (!RTStrNICmp(psz, "Diff", len))
174 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
175 else if (!RTStrNICmp(psz, "split2g", len))
176 uImageFlags |= VD_VMDK_IMAGE_FLAGS_SPLIT_2G;
177 else if ( !RTStrNICmp(psz, "stream", len)
178 || !RTStrNICmp(psz, "streamoptimized", len))
179 uImageFlags |= VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED;
180 else if (!RTStrNICmp(psz, "esx", len))
181 uImageFlags |= VD_VMDK_IMAGE_FLAGS_ESX;
182 else
183 rc = VERR_PARSE_ERROR;
184 }
185 if (pszComma)
186 psz += len + 1;
187 else
188 psz += len;
189 }
190
191 if (RT_SUCCESS(rc))
192 *puImageFlags = uImageFlags;
193 return rc;
194}
195
196
197static int handleSetUUID(HandlerArg *a)
198{
199 const char *pszFilename = NULL;
200 char *pszFormat = NULL;
201 VDTYPE enmType = VDTYPE_INVALID;
202 RTUUID imageUuid;
203 RTUUID parentUuid;
204 bool fSetImageUuid = false;
205 bool fSetParentUuid = false;
206 RTUuidClear(&imageUuid);
207 RTUuidClear(&parentUuid);
208 int rc;
209
210 /* Parse the command line. */
211 static const RTGETOPTDEF s_aOptions[] =
212 {
213 { "--filename", 'f', RTGETOPT_REQ_STRING },
214 { "--format", 'o', RTGETOPT_REQ_STRING },
215 { "--uuid", 'u', RTGETOPT_REQ_UUID },
216 { "--parentuuid", 'p', RTGETOPT_REQ_UUID },
217 { "--zeroparentuuid", 'P', RTGETOPT_REQ_NOTHING }
218 };
219 int ch;
220 RTGETOPTUNION ValueUnion;
221 RTGETOPTSTATE GetState;
222 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
223 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
224 {
225 switch (ch)
226 {
227 case 'f': // --filename
228 pszFilename = ValueUnion.psz;
229 break;
230 case 'o': // --format
231 pszFormat = RTStrDup(ValueUnion.psz);
232 break;
233 case 'u': // --uuid
234 imageUuid = ValueUnion.Uuid;
235 fSetImageUuid = true;
236 break;
237 case 'p': // --parentuuid
238 parentUuid = ValueUnion.Uuid;
239 fSetParentUuid = true;
240 break;
241 case 'P': // --zeroparentuuid
242 RTUuidClear(&parentUuid);
243 fSetParentUuid = true;
244 break;
245
246 default:
247 ch = RTGetOptPrintError(ch, &ValueUnion);
248 printUsage(g_pStdErr);
249 return ch;
250 }
251 }
252
253 /* Check for mandatory parameters. */
254 if (!pszFilename)
255 return errorSyntax("Mandatory --filename option missing\n");
256
257 /* Check for consistency of optional parameters. */
258 if (fSetImageUuid && RTUuidIsNull(&imageUuid))
259 return errorSyntax("Invalid parameter to --uuid option\n");
260
261 /* Autodetect image format. */
262 if (!pszFormat)
263 {
264 /* Don't pass error interface, as that would triggers error messages
265 * because some backends fail to open the image. */
266 rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat, &enmType);
267 if (RT_FAILURE(rc))
268 return errorRuntime("Format autodetect failed: %Rrc\n", rc);
269 }
270
271 PVBOXHDD pVD = NULL;
272 rc = VDCreate(pVDIfs, enmType, &pVD);
273 if (RT_FAILURE(rc))
274 return errorRuntime("Cannot create the virtual disk container: %Rrc\n", rc);
275
276 /* Open in info mode to be able to open diff images without their parent. */
277 rc = VDOpen(pVD, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO, NULL);
278 if (RT_FAILURE(rc))
279 return errorRuntime("Cannot open the virtual disk image \"%s\": %Rrc\n",
280 pszFilename, rc);
281
282 RTUUID oldImageUuid;
283 rc = VDGetUuid(pVD, VD_LAST_IMAGE, &oldImageUuid);
284 if (RT_FAILURE(rc))
285 return errorRuntime("Cannot get UUID of virtual disk image \"%s\": %Rrc\n",
286 pszFilename, rc);
287
288 RTPrintf("Old image UUID: %RTuuid\n", &oldImageUuid);
289
290 RTUUID oldParentUuid;
291 rc = VDGetParentUuid(pVD, VD_LAST_IMAGE, &oldParentUuid);
292 if (RT_FAILURE(rc))
293 return errorRuntime("Cannot get parent UUID of virtual disk image \"%s\": %Rrc\n",
294 pszFilename, rc);
295
296 RTPrintf("Old parent UUID: %RTuuid\n", &oldParentUuid);
297
298 if (fSetImageUuid)
299 {
300 RTPrintf("New image UUID: %RTuuid\n", &imageUuid);
301 rc = VDSetUuid(pVD, VD_LAST_IMAGE, &imageUuid);
302 if (RT_FAILURE(rc))
303 return errorRuntime("Cannot set UUID of virtual disk image \"%s\": %Rrc\n",
304 pszFilename, rc);
305 }
306
307 if (fSetParentUuid)
308 {
309 RTPrintf("New parent UUID: %RTuuid\n", &parentUuid);
310 rc = VDSetParentUuid(pVD, VD_LAST_IMAGE, &parentUuid);
311 if (RT_FAILURE(rc))
312 return errorRuntime("Cannot set parent UUID of virtual disk image \"%s\": %Rrc\n",
313 pszFilename, rc);
314 }
315
316 VDDestroy(pVD);
317
318 if (pszFormat)
319 {
320 RTStrFree(pszFormat);
321 pszFormat = NULL;
322 }
323
324 return 0;
325}
326
327
328static int handleGeometry(HandlerArg *a)
329{
330 const char *pszFilename = NULL;
331 char *pszFormat = NULL;
332 VDTYPE enmType = VDTYPE_INVALID;
333 uint16_t cCylinders = 0;
334 uint8_t cHeads = 0;
335 uint8_t cSectors = 0;
336 bool fCylinders = false;
337 bool fHeads = false;
338 bool fSectors = false;
339 int rc;
340
341 /* Parse the command line. */
342 static const RTGETOPTDEF s_aOptions[] =
343 {
344 { "--filename", 'f', RTGETOPT_REQ_STRING },
345 { "--format", 'o', RTGETOPT_REQ_STRING },
346 { "--clearchs", 'C', RTGETOPT_REQ_NOTHING },
347 { "--cylinders", 'c', RTGETOPT_REQ_UINT16 },
348 { "--heads", 'e', RTGETOPT_REQ_UINT8 },
349 { "--sectors", 's', RTGETOPT_REQ_UINT8 }
350 };
351 int ch;
352 RTGETOPTUNION ValueUnion;
353 RTGETOPTSTATE GetState;
354 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
355 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
356 {
357 switch (ch)
358 {
359 case 'f': // --filename
360 pszFilename = ValueUnion.psz;
361 break;
362 case 'o': // --format
363 pszFormat = RTStrDup(ValueUnion.psz);
364 break;
365 case 'C': // --clearchs
366 cCylinders = 0;
367 cHeads = 0;
368 cSectors = 0;
369 fCylinders = true;
370 fHeads = true;
371 fSectors = true;
372 break;
373 case 'c': // --cylinders
374 cCylinders = ValueUnion.u16;
375 fCylinders = true;
376 break;
377 case 'e': // --heads
378 cHeads = ValueUnion.u8;
379 fHeads = true;
380 break;
381 case 's': // --sectors
382 cSectors = ValueUnion.u8;
383 fSectors = true;
384 break;
385
386 default:
387 ch = RTGetOptPrintError(ch, &ValueUnion);
388 printUsage(g_pStdErr);
389 return ch;
390 }
391 }
392
393 /* Check for mandatory parameters. */
394 if (!pszFilename)
395 return errorSyntax("Mandatory --filename option missing\n");
396
397 /* Autodetect image format. */
398 if (!pszFormat)
399 {
400 /* Don't pass error interface, as that would triggers error messages
401 * because some backends fail to open the image. */
402 rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat, &enmType);
403 if (RT_FAILURE(rc))
404 return errorRuntime("Format autodetect failed: %Rrc\n", rc);
405 }
406
407 PVBOXHDD pVD = NULL;
408 rc = VDCreate(pVDIfs, enmType, &pVD);
409 if (RT_FAILURE(rc))
410 return errorRuntime("Cannot create the virtual disk container: %Rrc\n", rc);
411
412 /* Open in info mode to be able to open diff images without their parent. */
413 rc = VDOpen(pVD, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO, NULL);
414 if (RT_FAILURE(rc))
415 return errorRuntime("Cannot open the virtual disk image \"%s\": %Rrc\n",
416 pszFilename, rc);
417
418 VDGEOMETRY oldLCHSGeometry;
419 rc = VDGetLCHSGeometry(pVD, VD_LAST_IMAGE, &oldLCHSGeometry);
420 if (rc == VERR_VD_GEOMETRY_NOT_SET)
421 {
422 memset(&oldLCHSGeometry, 0, sizeof(oldLCHSGeometry));
423 rc = VINF_SUCCESS;
424 }
425 if (RT_FAILURE(rc))
426 return errorRuntime("Cannot get LCHS geometry of virtual disk image \"%s\": %Rrc\n",
427 pszFilename, rc);
428
429 VDGEOMETRY newLCHSGeometry = oldLCHSGeometry;
430 if (fCylinders)
431 newLCHSGeometry.cCylinders = cCylinders;
432 if (fHeads)
433 newLCHSGeometry.cHeads = cHeads;
434 if (fSectors)
435 newLCHSGeometry.cSectors = cSectors;
436
437 if (fCylinders || fHeads || fSectors)
438 {
439 RTPrintf("Old image LCHS: %u/%u/%u\n", oldLCHSGeometry.cCylinders, oldLCHSGeometry.cHeads, oldLCHSGeometry.cSectors);
440 RTPrintf("New image LCHS: %u/%u/%u\n", newLCHSGeometry.cCylinders, newLCHSGeometry.cHeads, newLCHSGeometry.cSectors);
441
442 rc = VDSetLCHSGeometry(pVD, VD_LAST_IMAGE, &newLCHSGeometry);
443 if (RT_FAILURE(rc))
444 return errorRuntime("Cannot set LCHS geometry of virtual disk image \"%s\": %Rrc\n",
445 pszFilename, rc);
446 }
447 else
448 RTPrintf("Current image LCHS: %u/%u/%u\n", oldLCHSGeometry.cCylinders, oldLCHSGeometry.cHeads, oldLCHSGeometry.cSectors);
449
450
451 VDDestroy(pVD);
452
453 if (pszFormat)
454 {
455 RTStrFree(pszFormat);
456 pszFormat = NULL;
457 }
458
459 return 0;
460}
461
462
463typedef struct FILEIOSTATE
464{
465 RTFILE file;
466 /** Offset in the file. */
467 uint64_t off;
468 /** Offset where the buffer contents start. UINT64_MAX=buffer invalid. */
469 uint64_t offBuffer;
470 /** Size of valid data in the buffer. */
471 uint32_t cbBuffer;
472 /** Buffer for efficient I/O */
473 uint8_t abBuffer[16 *_1M];
474} FILEIOSTATE, *PFILEIOSTATE;
475
476static int convInOpen(void *pvUser, const char *pszLocation,
477 uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
478 void **ppStorage)
479{
480 NOREF(pvUser);
481 /* Validate input. */
482 AssertPtrReturn(ppStorage, VERR_INVALID_POINTER);
483 AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
484 AssertReturn((fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ, VERR_INVALID_PARAMETER);
485 RTFILE file;
486 int rc = RTFileFromNative(&file, RTFILE_NATIVE_STDIN);
487 if (RT_FAILURE(rc))
488 return rc;
489
490 /* No need to clear the buffer, the data will be read from disk. */
491 PFILEIOSTATE pFS = (PFILEIOSTATE)RTMemAlloc(sizeof(FILEIOSTATE));
492 if (!pFS)
493 return VERR_NO_MEMORY;
494
495 pFS->file = file;
496 pFS->off = 0;
497 pFS->offBuffer = UINT64_MAX;
498 pFS->cbBuffer = 0;
499
500 *ppStorage = pFS;
501 return VINF_SUCCESS;
502}
503
504static int convInClose(void *pvUser, void *pStorage)
505{
506 NOREF(pvUser);
507 AssertPtrReturn(pStorage, VERR_INVALID_POINTER);
508 PFILEIOSTATE pFS = (PFILEIOSTATE)pStorage;
509
510 RTMemFree(pFS);
511
512 return VINF_SUCCESS;
513}
514
515static int convInDelete(void *pvUser, const char *pcszFilename)
516{
517 NOREF(pvUser);
518 NOREF(pcszFilename);
519 AssertFailedReturn(VERR_NOT_SUPPORTED);
520}
521
522static int convInMove(void *pvUser, const char *pcszSrc, const char *pcszDst,
523 unsigned fMove)
524{
525 NOREF(pvUser);
526 NOREF(pcszSrc);
527 NOREF(pcszDst);
528 NOREF(fMove);
529 AssertFailedReturn(VERR_NOT_SUPPORTED);
530}
531
532static int convInGetFreeSpace(void *pvUser, const char *pcszFilename,
533 int64_t *pcbFreeSpace)
534{
535 NOREF(pvUser);
536 NOREF(pcszFilename);
537 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
538 *pcbFreeSpace = 0;
539 return VINF_SUCCESS;
540}
541
542static int convInGetModificationTime(void *pvUser, const char *pcszFilename,
543 PRTTIMESPEC pModificationTime)
544{
545 NOREF(pvUser);
546 NOREF(pcszFilename);
547 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
548 AssertFailedReturn(VERR_NOT_SUPPORTED);
549}
550
551static int convInGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
552{
553 NOREF(pvUser);
554 NOREF(pStorage);
555 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
556 AssertFailedReturn(VERR_NOT_SUPPORTED);
557}
558
559static int convInSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
560{
561 NOREF(pvUser);
562 NOREF(pStorage);
563 NOREF(cbSize);
564 AssertFailedReturn(VERR_NOT_SUPPORTED);
565}
566
567static int convInRead(void *pvUser, void *pStorage, uint64_t uOffset,
568 void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
569{
570 NOREF(pvUser);
571 AssertPtrReturn(pStorage, VERR_INVALID_POINTER);
572 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
573 PFILEIOSTATE pFS = (PFILEIOSTATE)pStorage;
574 AssertReturn(uOffset >= pFS->off, VERR_INVALID_PARAMETER);
575 int rc;
576
577 /* Fill buffer if it is empty. */
578 if (pFS->offBuffer == UINT64_MAX)
579 {
580 /* Repeat reading until buffer is full or EOF. */
581 size_t cbRead;
582 size_t cbSumRead = 0;
583 uint8_t *pbTmp = (uint8_t *)&pFS->abBuffer[0];
584 size_t cbTmp = sizeof(pFS->abBuffer);
585 do
586 {
587 rc = RTFileRead(pFS->file, pbTmp, cbTmp, &cbRead);
588 if (RT_FAILURE(rc))
589 return rc;
590 pbTmp += cbRead;
591 cbTmp -= cbRead;
592 cbSumRead += cbRead;
593 } while (cbTmp && cbRead);
594
595 pFS->offBuffer = 0;
596 pFS->cbBuffer = (uint32_t)cbSumRead;
597 if (!cbSumRead && !pcbRead) /* Caller can't handle partial reads. */
598 return VERR_EOF;
599 }
600
601 /* Read several blocks and assemble the result if necessary */
602 size_t cbTotalRead = 0;
603 do
604 {
605 /* Skip over areas no one wants to read. */
606 while (uOffset > pFS->offBuffer + pFS->cbBuffer - 1)
607 {
608 if (pFS->cbBuffer < sizeof(pFS->abBuffer))
609 {
610 if (pcbRead)
611 *pcbRead = cbTotalRead;
612 return VERR_EOF;
613 }
614
615 /* Repeat reading until buffer is full or EOF. */
616 size_t cbRead;
617 size_t cbSumRead = 0;
618 uint8_t *pbTmp = (uint8_t *)&pFS->abBuffer[0];
619 size_t cbTmp = sizeof(pFS->abBuffer);
620 do
621 {
622 rc = RTFileRead(pFS->file, pbTmp, cbTmp, &cbRead);
623 if (RT_FAILURE(rc))
624 return rc;
625 pbTmp += cbRead;
626 cbTmp -= cbRead;
627 cbSumRead += cbRead;
628 } while (cbTmp && cbRead);
629
630 pFS->offBuffer += pFS->cbBuffer;
631 pFS->cbBuffer = (uint32_t)cbSumRead;
632 }
633
634 uint32_t cbThisRead = (uint32_t)RT_MIN(cbBuffer,
635 pFS->cbBuffer - uOffset % sizeof(pFS->abBuffer));
636 memcpy(pvBuffer, &pFS->abBuffer[uOffset % sizeof(pFS->abBuffer)],
637 cbThisRead);
638 uOffset += cbThisRead;
639 pvBuffer = (uint8_t *)pvBuffer + cbThisRead;
640 cbBuffer -= cbThisRead;
641 cbTotalRead += cbThisRead;
642 if (!cbTotalRead && !pcbRead) /* Caller can't handle partial reads. */
643 return VERR_EOF;
644 } while (cbBuffer > 0);
645
646 if (pcbRead)
647 *pcbRead = cbTotalRead;
648
649 pFS->off = uOffset;
650
651 return VINF_SUCCESS;
652}
653
654static int convInWrite(void *pvUser, void *pStorage, uint64_t uOffset,
655 const void *pvBuffer, size_t cbBuffer,
656 size_t *pcbWritten)
657{
658 NOREF(pvUser);
659 NOREF(pStorage);
660 NOREF(uOffset);
661 NOREF(cbBuffer);
662 NOREF(pcbWritten);
663 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
664 AssertFailedReturn(VERR_NOT_SUPPORTED);
665}
666
667static int convInFlush(void *pvUser, void *pStorage)
668{
669 NOREF(pvUser);
670 NOREF(pStorage);
671 return VINF_SUCCESS;
672}
673
674static int convOutOpen(void *pvUser, const char *pszLocation,
675 uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
676 void **ppStorage)
677{
678 NOREF(pvUser);
679 /* Validate input. */
680 AssertPtrReturn(ppStorage, VERR_INVALID_POINTER);
681 AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
682 AssertReturn((fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_WRITE, VERR_INVALID_PARAMETER);
683 RTFILE file;
684 int rc = RTFileFromNative(&file, RTFILE_NATIVE_STDOUT);
685 if (RT_FAILURE(rc))
686 return rc;
687
688 /* Must clear buffer, so that skipped over data is initialized properly. */
689 PFILEIOSTATE pFS = (PFILEIOSTATE)RTMemAllocZ(sizeof(FILEIOSTATE));
690 if (!pFS)
691 return VERR_NO_MEMORY;
692
693 pFS->file = file;
694 pFS->off = 0;
695 pFS->offBuffer = 0;
696 pFS->cbBuffer = sizeof(FILEIOSTATE);
697
698 *ppStorage = pFS;
699 return VINF_SUCCESS;
700}
701
702static int convOutClose(void *pvUser, void *pStorage)
703{
704 NOREF(pvUser);
705 AssertPtrReturn(pStorage, VERR_INVALID_POINTER);
706 PFILEIOSTATE pFS = (PFILEIOSTATE)pStorage;
707 int rc = VINF_SUCCESS;
708
709 /* Flush any remaining buffer contents. */
710 if (pFS->cbBuffer)
711 rc = RTFileWrite(pFS->file, &pFS->abBuffer[0], pFS->cbBuffer, NULL);
712
713 RTMemFree(pFS);
714
715 return rc;
716}
717
718static int convOutDelete(void *pvUser, const char *pcszFilename)
719{
720 NOREF(pvUser);
721 NOREF(pcszFilename);
722 AssertFailedReturn(VERR_NOT_SUPPORTED);
723}
724
725static int convOutMove(void *pvUser, const char *pcszSrc, const char *pcszDst,
726 unsigned fMove)
727{
728 NOREF(pvUser);
729 NOREF(pcszSrc);
730 NOREF(pcszDst);
731 NOREF(fMove);
732 AssertFailedReturn(VERR_NOT_SUPPORTED);
733}
734
735static int convOutGetFreeSpace(void *pvUser, const char *pcszFilename,
736 int64_t *pcbFreeSpace)
737{
738 NOREF(pvUser);
739 NOREF(pcszFilename);
740 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
741 *pcbFreeSpace = INT64_MAX;
742 return VINF_SUCCESS;
743}
744
745static int convOutGetModificationTime(void *pvUser, const char *pcszFilename,
746 PRTTIMESPEC pModificationTime)
747{
748 NOREF(pvUser);
749 NOREF(pcszFilename);
750 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
751 AssertFailedReturn(VERR_NOT_SUPPORTED);
752}
753
754static int convOutGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
755{
756 NOREF(pvUser);
757 NOREF(pStorage);
758 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
759 AssertFailedReturn(VERR_NOT_SUPPORTED);
760}
761
762static int convOutSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
763{
764 NOREF(pvUser);
765 NOREF(pStorage);
766 NOREF(cbSize);
767 AssertFailedReturn(VERR_NOT_SUPPORTED);
768}
769
770static int convOutRead(void *pvUser, void *pStorage, uint64_t uOffset,
771 void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
772{
773 NOREF(pvUser);
774 NOREF(pStorage);
775 NOREF(uOffset);
776 NOREF(cbBuffer);
777 NOREF(pcbRead);
778 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
779 AssertFailedReturn(VERR_NOT_SUPPORTED);
780}
781
782static int convOutWrite(void *pvUser, void *pStorage, uint64_t uOffset,
783 const void *pvBuffer, size_t cbBuffer,
784 size_t *pcbWritten)
785{
786 NOREF(pvUser);
787 AssertPtrReturn(pStorage, VERR_INVALID_POINTER);
788 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
789 PFILEIOSTATE pFS = (PFILEIOSTATE)pStorage;
790 AssertReturn(uOffset >= pFS->off, VERR_INVALID_PARAMETER);
791 int rc;
792
793 /* Write the data to the buffer, flushing as required. */
794 size_t cbTotalWritten = 0;
795 do
796 {
797 /* Flush the buffer if we need a new one. */
798 while (uOffset > pFS->offBuffer + sizeof(pFS->abBuffer) - 1)
799 {
800 rc = RTFileWrite(pFS->file, &pFS->abBuffer[0],
801 sizeof(pFS->abBuffer), NULL);
802 RT_ZERO(pFS->abBuffer);
803 pFS->offBuffer += sizeof(pFS->abBuffer);
804 pFS->cbBuffer = 0;
805 }
806
807 uint32_t cbThisWrite = (uint32_t)RT_MIN(cbBuffer,
808 sizeof(pFS->abBuffer) - uOffset % sizeof(pFS->abBuffer));
809 memcpy(&pFS->abBuffer[uOffset % sizeof(pFS->abBuffer)], pvBuffer,
810 cbThisWrite);
811 uOffset += cbThisWrite;
812 pvBuffer = (uint8_t *)pvBuffer + cbThisWrite;
813 cbBuffer -= cbThisWrite;
814 cbTotalWritten += cbThisWrite;
815 } while (cbBuffer > 0);
816
817 if (pcbWritten)
818 *pcbWritten = cbTotalWritten;
819
820 pFS->cbBuffer = uOffset % sizeof(pFS->abBuffer);
821 if (!pFS->cbBuffer)
822 pFS->cbBuffer = sizeof(pFS->abBuffer);
823 pFS->off = uOffset;
824
825 return VINF_SUCCESS;
826}
827
828static int convOutFlush(void *pvUser, void *pStorage)
829{
830 NOREF(pvUser);
831 NOREF(pStorage);
832 return VINF_SUCCESS;
833}
834
835static int handleConvert(HandlerArg *a)
836{
837 const char *pszSrcFilename = NULL;
838 const char *pszDstFilename = NULL;
839 bool fStdIn = false;
840 bool fStdOut = false;
841 const char *pszSrcFormat = NULL;
842 VDTYPE enmSrcType = VDTYPE_HDD;
843 const char *pszDstFormat = NULL;
844 const char *pszVariant = NULL;
845 PVBOXHDD pSrcDisk = NULL;
846 PVBOXHDD pDstDisk = NULL;
847 unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
848 PVDINTERFACE pIfsImageInput = NULL;
849 PVDINTERFACE pIfsImageOutput = NULL;
850 VDINTERFACEIO IfsInputIO;
851 VDINTERFACEIO IfsOutputIO;
852 int rc = VINF_SUCCESS;
853
854 /* Parse the command line. */
855 static const RTGETOPTDEF s_aOptions[] =
856 {
857 { "--srcfilename", 'i', RTGETOPT_REQ_STRING },
858 { "--dstfilename", 'o', RTGETOPT_REQ_STRING },
859 { "--stdin", 'p', RTGETOPT_REQ_NOTHING },
860 { "--stdout", 'P', RTGETOPT_REQ_NOTHING },
861 { "--srcformat", 's', RTGETOPT_REQ_STRING },
862 { "--dstformat", 'd', RTGETOPT_REQ_STRING },
863 { "--variant", 'v', RTGETOPT_REQ_STRING }
864 };
865 int ch;
866 RTGETOPTUNION ValueUnion;
867 RTGETOPTSTATE GetState;
868 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
869 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
870 {
871 switch (ch)
872 {
873 case 'i': // --srcfilename
874 pszSrcFilename = ValueUnion.psz;
875 break;
876 case 'o': // --dstfilename
877 pszDstFilename = ValueUnion.psz;
878 break;
879 case 'p': // --stdin
880 fStdIn = true;
881 break;
882 case 'P': // --stdout
883 fStdOut = true;
884 break;
885 case 's': // --srcformat
886 pszSrcFormat = ValueUnion.psz;
887 break;
888 case 'd': // --dstformat
889 pszDstFormat = ValueUnion.psz;
890 break;
891 case 'v': // --variant
892 pszVariant = ValueUnion.psz;
893 break;
894
895 default:
896 ch = RTGetOptPrintError(ch, &ValueUnion);
897 printUsage(g_pStdErr);
898 return ch;
899 }
900 }
901
902 /* Check for mandatory parameters and handle dummies/defaults. */
903 if (fStdIn && !pszSrcFormat)
904 return errorSyntax("Mandatory --srcformat option missing\n");
905 if (!pszDstFormat)
906 pszDstFormat = "VDI";
907 if (fStdIn && !pszSrcFilename)
908 {
909 /* Complete dummy, will be just passed to various calls to fulfill
910 * the "must be non-NULL" requirement, and is completely ignored
911 * otherwise. It shown in the stderr message below. */
912 pszSrcFilename = "stdin";
913 }
914 if (fStdOut && !pszDstFilename)
915 {
916 /* Will be stored in the destination image if it is a streamOptimized
917 * VMDK, but it isn't really relevant - use it for "branding". */
918 if (!RTStrICmp(pszDstFormat, "VMDK"))
919 pszDstFilename = "VirtualBoxStream.vmdk";
920 else
921 pszDstFilename = "stdout";
922 }
923 if (!pszSrcFilename)
924 return errorSyntax("Mandatory --srcfilename option missing\n");
925 if (!pszDstFilename)
926 return errorSyntax("Mandatory --dstfilename option missing\n");
927
928 if (fStdIn)
929 {
930 IfsInputIO.pfnOpen = convInOpen;
931 IfsInputIO.pfnClose = convInClose;
932 IfsInputIO.pfnDelete = convInDelete;
933 IfsInputIO.pfnMove = convInMove;
934 IfsInputIO.pfnGetFreeSpace = convInGetFreeSpace;
935 IfsInputIO.pfnGetModificationTime = convInGetModificationTime;
936 IfsInputIO.pfnGetSize = convInGetSize;
937 IfsInputIO.pfnSetSize = convInSetSize;
938 IfsInputIO.pfnReadSync = convInRead;
939 IfsInputIO.pfnWriteSync = convInWrite;
940 IfsInputIO.pfnFlushSync = convInFlush;
941 VDInterfaceAdd(&IfsInputIO.Core, "stdin", VDINTERFACETYPE_IO,
942 NULL, sizeof(VDINTERFACEIO), &pIfsImageInput);
943 }
944 if (fStdOut)
945 {
946 IfsOutputIO.pfnOpen = convOutOpen;
947 IfsOutputIO.pfnClose = convOutClose;
948 IfsOutputIO.pfnDelete = convOutDelete;
949 IfsOutputIO.pfnMove = convOutMove;
950 IfsOutputIO.pfnGetFreeSpace = convOutGetFreeSpace;
951 IfsOutputIO.pfnGetModificationTime = convOutGetModificationTime;
952 IfsOutputIO.pfnGetSize = convOutGetSize;
953 IfsOutputIO.pfnSetSize = convOutSetSize;
954 IfsOutputIO.pfnReadSync = convOutRead;
955 IfsOutputIO.pfnWriteSync = convOutWrite;
956 IfsOutputIO.pfnFlushSync = convOutFlush;
957 VDInterfaceAdd(&IfsOutputIO.Core, "stdout", VDINTERFACETYPE_IO,
958 NULL, sizeof(VDINTERFACEIO), &pIfsImageOutput);
959 }
960
961 /* check the variant parameter */
962 if (pszVariant)
963 {
964 char *psz = (char*)pszVariant;
965 while (psz && *psz && RT_SUCCESS(rc))
966 {
967 size_t len;
968 const char *pszComma = strchr(psz, ',');
969 if (pszComma)
970 len = pszComma - psz;
971 else
972 len = strlen(psz);
973 if (len > 0)
974 {
975 if (!RTStrNICmp(pszVariant, "standard", len))
976 uImageFlags |= VD_IMAGE_FLAGS_NONE;
977 else if (!RTStrNICmp(pszVariant, "fixed", len))
978 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
979 else if (!RTStrNICmp(pszVariant, "split2g", len))
980 uImageFlags |= VD_VMDK_IMAGE_FLAGS_SPLIT_2G;
981 else if (!RTStrNICmp(pszVariant, "stream", len))
982 uImageFlags |= VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED;
983 else if (!RTStrNICmp(pszVariant, "esx", len))
984 uImageFlags |= VD_VMDK_IMAGE_FLAGS_ESX;
985 else
986 return errorSyntax("Invalid --variant option\n");
987 }
988 if (pszComma)
989 psz += len + 1;
990 else
991 psz += len;
992 }
993 }
994
995 do
996 {
997 /* try to determine input format if not specified */
998 if (!pszSrcFormat)
999 {
1000 char *pszFormat = NULL;
1001 VDTYPE enmType = VDTYPE_INVALID;
1002 rc = VDGetFormat(NULL, NULL, pszSrcFilename, &pszFormat, &enmType);
1003 if (RT_FAILURE(rc))
1004 {
1005 errorSyntax("No file format specified, please specify format: %Rrc\n", rc);
1006 break;
1007 }
1008 pszSrcFormat = pszFormat;
1009 enmSrcType = enmType;
1010 }
1011
1012 rc = VDCreate(pVDIfs, enmSrcType, &pSrcDisk);
1013 if (RT_FAILURE(rc))
1014 {
1015 errorRuntime("Error while creating source disk container: %Rrc\n", rc);
1016 break;
1017 }
1018
1019 rc = VDOpen(pSrcDisk, pszSrcFormat, pszSrcFilename,
1020 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL,
1021 pIfsImageInput);
1022 if (RT_FAILURE(rc))
1023 {
1024 errorRuntime("Error while opening source image: %Rrc\n", rc);
1025 break;
1026 }
1027
1028 rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDstDisk);
1029 if (RT_FAILURE(rc))
1030 {
1031 errorRuntime("Error while creating the destination disk container: %Rrc\n", rc);
1032 break;
1033 }
1034
1035 uint64_t cbSize = VDGetSize(pSrcDisk, VD_LAST_IMAGE);
1036 RTStrmPrintf(g_pStdErr, "Converting image \"%s\" with size %RU64 bytes (%RU64MB)...\n", pszSrcFilename, cbSize, (cbSize + _1M - 1) / _1M);
1037
1038 /* Create the output image */
1039 rc = VDCopy(pSrcDisk, VD_LAST_IMAGE, pDstDisk, pszDstFormat,
1040 pszDstFilename, false, 0, uImageFlags, NULL,
1041 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL, NULL,
1042 pIfsImageOutput, NULL);
1043 if (RT_FAILURE(rc))
1044 {
1045 errorRuntime("Error while copying the image: %Rrc\n", rc);
1046 break;
1047 }
1048
1049 }
1050 while (0);
1051
1052 if (pDstDisk)
1053 VDDestroy(pDstDisk);
1054 if (pSrcDisk)
1055 VDDestroy(pSrcDisk);
1056
1057 return RT_SUCCESS(rc) ? 0 : 1;
1058}
1059
1060
1061static int handleInfo(HandlerArg *a)
1062{
1063 int rc = VINF_SUCCESS;
1064 PVBOXHDD pDisk = NULL;
1065 const char *pszFilename = NULL;
1066
1067 /* Parse the command line. */
1068 static const RTGETOPTDEF s_aOptions[] =
1069 {
1070 { "--filename", 'f', RTGETOPT_REQ_STRING }
1071 };
1072 int ch;
1073 RTGETOPTUNION ValueUnion;
1074 RTGETOPTSTATE GetState;
1075 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1076 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1077 {
1078 switch (ch)
1079 {
1080 case 'f': // --filename
1081 pszFilename = ValueUnion.psz;
1082 break;
1083
1084 default:
1085 ch = RTGetOptPrintError(ch, &ValueUnion);
1086 printUsage(g_pStdErr);
1087 return ch;
1088 }
1089 }
1090
1091 /* Check for mandatory parameters. */
1092 if (!pszFilename)
1093 return errorSyntax("Mandatory --filename option missing\n");
1094
1095 /* just try it */
1096 char *pszFormat = NULL;
1097 VDTYPE enmType = VDTYPE_INVALID;
1098 rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat, &enmType);
1099 if (RT_FAILURE(rc))
1100 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
1101
1102 rc = VDCreate(pVDIfs, enmType, &pDisk);
1103 if (RT_FAILURE(rc))
1104 return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
1105
1106 /* Open the image */
1107 rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY, NULL);
1108 if (RT_FAILURE(rc))
1109 return errorRuntime("Error while opening the image: %Rrc\n", rc);
1110
1111 VDDumpImages(pDisk);
1112
1113 VDDestroy(pDisk);
1114
1115 return rc;
1116}
1117
1118
1119static DECLCALLBACK(int) vboximgDvmRead(void *pvUser, uint64_t off, void *pvBuf, size_t cbRead)
1120{
1121 int rc = VINF_SUCCESS;
1122 PVBOXHDD pDisk = (PVBOXHDD)pvUser;
1123
1124 /* Take shortcut if possible. */
1125 if ( off % 512 == 0
1126 && cbRead % 512 == 0)
1127 rc = VDRead(pDisk, off, pvBuf, cbRead);
1128 else
1129 {
1130 uint8_t *pbBuf = (uint8_t *)pvBuf;
1131 uint8_t abBuf[512];
1132
1133 /* Unaligned access, make it aligned. */
1134 if (off % 512 != 0)
1135 {
1136 uint64_t offAligned = off & ~(uint64_t)(512 - 1);
1137 size_t cbToCopy = 512 - (off - offAligned);
1138 rc = VDRead(pDisk, offAligned, abBuf, 512);
1139 if (RT_SUCCESS(rc))
1140 {
1141 memcpy(pbBuf, &abBuf[off - offAligned], cbToCopy);
1142 pbBuf += cbToCopy;
1143 off += cbToCopy;
1144 cbRead -= cbToCopy;
1145 }
1146 }
1147
1148 if ( RT_SUCCESS(rc)
1149 && (cbRead & ~(uint64_t)(512 - 1)))
1150 {
1151 size_t cbReadAligned = cbRead & ~(uint64_t)(512 - 1);
1152
1153 Assert(!(off % 512));
1154 rc = VDRead(pDisk, off, pbBuf, cbReadAligned);
1155 if (RT_SUCCESS(rc))
1156 {
1157 pbBuf += cbReadAligned;
1158 off += cbReadAligned;
1159 cbRead -= cbReadAligned;
1160 }
1161 }
1162
1163 if ( RT_SUCCESS(rc)
1164 && cbRead)
1165 {
1166 Assert(cbRead < 512);
1167 Assert(!(off % 512));
1168
1169 rc = VDRead(pDisk, off, abBuf, 512);
1170 if (RT_SUCCESS(rc))
1171 memcpy(pbBuf, abBuf, cbRead);
1172 }
1173 }
1174
1175 return rc;
1176}
1177
1178
1179static DECLCALLBACK(int) vboximgDvmWrite(void *pvUser, uint64_t off, const void *pvBuf, size_t cbWrite)
1180{
1181 PVBOXHDD pDisk = (PVBOXHDD)pvUser;
1182 return VDWrite(pDisk, off, pvBuf, cbWrite);
1183}
1184
1185
1186static DECLCALLBACK(int) vboximgQueryBlockStatus(void *pvUser, uint64_t off,
1187 uint64_t cb, bool *pfAllocated)
1188{
1189 RTVFS hVfs = (RTVFS)pvUser;
1190 return RTVfsIsRangeInUse(hVfs, off, cb, pfAllocated);
1191}
1192
1193
1194static DECLCALLBACK(int) vboximgQueryRangeUse(void *pvUser, uint64_t off, uint64_t cb,
1195 bool *pfUsed)
1196{
1197 RTDVM hVolMgr = (RTDVM)pvUser;
1198 return RTDvmMapQueryBlockStatus(hVolMgr, off, cb, pfUsed);
1199}
1200
1201
1202typedef struct VBOXIMGVFS
1203{
1204 /** Pointer to the next VFS handle. */
1205 struct VBOXIMGVFS *pNext;
1206 /** VFS handle. */
1207 RTVFS hVfs;
1208} VBOXIMGVFS, *PVBOXIMGVFS;
1209
1210static int handleCompact(HandlerArg *a)
1211{
1212 int rc = VINF_SUCCESS;
1213 PVBOXHDD pDisk = NULL;
1214 const char *pszFilename = NULL;
1215 bool fFilesystemAware = false;
1216 VDINTERFACEQUERYRANGEUSE VDIfQueryRangeUse;
1217 PVDINTERFACE pIfsCompact = NULL;
1218 RTDVM hDvm = NIL_RTDVM;
1219 PVBOXIMGVFS pVBoxImgVfsHead = NULL;
1220
1221 /* Parse the command line. */
1222 static const RTGETOPTDEF s_aOptions[] =
1223 {
1224 { "--filename", 'f', RTGETOPT_REQ_STRING },
1225 { "--filesystemaware", 'a', RTGETOPT_REQ_NOTHING }
1226 };
1227 int ch;
1228 RTGETOPTUNION ValueUnion;
1229 RTGETOPTSTATE GetState;
1230 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1231 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1232 {
1233 switch (ch)
1234 {
1235 case 'f': // --filename
1236 pszFilename = ValueUnion.psz;
1237 break;
1238
1239 case 'a':
1240 fFilesystemAware = true;
1241 break;
1242
1243 default:
1244 ch = RTGetOptPrintError(ch, &ValueUnion);
1245 printUsage(g_pStdErr);
1246 return ch;
1247 }
1248 }
1249
1250 /* Check for mandatory parameters. */
1251 if (!pszFilename)
1252 return errorSyntax("Mandatory --filename option missing\n");
1253
1254 /* just try it */
1255 char *pszFormat = NULL;
1256 VDTYPE enmType = VDTYPE_INVALID;
1257 rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat, &enmType);
1258 if (RT_FAILURE(rc))
1259 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
1260
1261 rc = VDCreate(pVDIfs, enmType, &pDisk);
1262 if (RT_FAILURE(rc))
1263 return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
1264
1265 /* Open the image */
1266 rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
1267 if (RT_FAILURE(rc))
1268 return errorRuntime("Error while opening the image: %Rrc\n", rc);
1269
1270 if ( RT_SUCCESS(rc)
1271 && fFilesystemAware)
1272 {
1273 uint64_t cbDisk = 0;
1274
1275 cbDisk = VDGetSize(pDisk, 0);
1276 if (cbDisk > 0)
1277 {
1278 rc = RTDvmCreate(&hDvm, vboximgDvmRead, vboximgDvmWrite, cbDisk, 512,
1279 0 /* fFlags*/, pDisk);
1280 if (RT_SUCCESS(rc))
1281 {
1282 rc = RTDvmMapOpen(hDvm);
1283 if ( RT_SUCCESS(rc)
1284 && RTDvmMapGetValidVolumes(hDvm))
1285 {
1286 RTDVMVOLUME hVol;
1287
1288 /* Get all volumes and set the block query status callback. */
1289 rc = RTDvmMapQueryFirstVolume(hDvm, &hVol);
1290 AssertRC(rc);
1291
1292 do
1293 {
1294 RTVFSFILE hVfsFile;
1295 RTVFS hVfs;
1296 RTDVMVOLUME hVolNext;
1297
1298 rc = RTDvmVolumeCreateVfsFile(hVol, &hVfsFile);
1299 if (RT_FAILURE(rc))
1300 break;
1301
1302 /* Try to detect the filesystem in this volume. */
1303 rc = RTFilesystemVfsFromFile(hVfsFile, &hVfs);
1304 if (rc == VERR_NOT_SUPPORTED)
1305 {
1306 /* Release the file handle and continue.*/
1307 RTVfsFileRelease(hVfsFile);
1308 }
1309 else if (RT_FAILURE(rc))
1310 break;
1311 else
1312 {
1313 PVBOXIMGVFS pVBoxImgVfs = (PVBOXIMGVFS)RTMemAllocZ(sizeof(VBOXIMGVFS));
1314 if (!pVBoxImgVfs)
1315 rc = VERR_NO_MEMORY;
1316 else
1317 {
1318 pVBoxImgVfs->hVfs = hVfs;
1319 pVBoxImgVfs->pNext = pVBoxImgVfsHead;
1320 pVBoxImgVfsHead = pVBoxImgVfs;
1321 RTDvmVolumeSetQueryBlockStatusCallback(hVol, vboximgQueryBlockStatus, hVfs);
1322 }
1323 }
1324
1325 if (RT_SUCCESS(rc))
1326 rc = RTDvmMapQueryNextVolume(hDvm, hVol, &hVolNext);
1327
1328 /*
1329 * Release the volume handle, the file handle has a reference
1330 * to keep it open.
1331 */
1332 RTDvmVolumeRelease(hVol);
1333 hVol = hVolNext;
1334 } while (RT_SUCCESS(rc));
1335
1336 if (rc == VERR_DVM_MAP_NO_VOLUME)
1337 rc = VINF_SUCCESS;
1338
1339 if (RT_SUCCESS(rc))
1340 {
1341 VDIfQueryRangeUse.pfnQueryRangeUse = vboximgQueryRangeUse;
1342 VDInterfaceAdd(&VDIfQueryRangeUse.Core, "QueryRangeUse", VDINTERFACETYPE_QUERYRANGEUSE,
1343 hDvm, sizeof(VDINTERFACEQUERYRANGEUSE), &pIfsCompact);
1344 }
1345 }
1346 else if (RT_SUCCESS(rc))
1347 RTPrintf("There are no partitions in the volume map\n");
1348 else if (rc == VERR_NOT_FOUND)
1349 {
1350 rc = VINF_SUCCESS;
1351 RTPrintf("No known volume format on disk found\n");
1352 }
1353 else
1354 errorRuntime("Error while opening the volume manager: %Rrc\n", rc);
1355 }
1356 else
1357 errorRuntime("Error creating the volume manager: %Rrc\n", rc);
1358 }
1359 else
1360 {
1361 rc = VERR_INVALID_STATE;
1362 errorRuntime("Error while getting the disk size\n");
1363 }
1364 }
1365
1366 if (RT_SUCCESS(rc))
1367 {
1368 rc = VDCompact(pDisk, 0, pIfsCompact);
1369 if (RT_FAILURE(rc))
1370 errorRuntime("Error while compacting image: %Rrc\n", rc);
1371 }
1372
1373 while (pVBoxImgVfsHead)
1374 {
1375 PVBOXIMGVFS pVBoxImgVfsFree = pVBoxImgVfsHead;
1376
1377 pVBoxImgVfsHead = pVBoxImgVfsHead->pNext;
1378 RTVfsRelease(pVBoxImgVfsFree->hVfs);
1379 RTMemFree(pVBoxImgVfsFree);
1380 }
1381
1382 if (hDvm)
1383 RTDvmRelease(hDvm);
1384
1385 VDDestroy(pDisk);
1386
1387 return rc;
1388}
1389
1390
1391static int handleCreateCache(HandlerArg *a)
1392{
1393 int rc = VINF_SUCCESS;
1394 PVBOXHDD pDisk = NULL;
1395 const char *pszFilename = NULL;
1396 uint64_t cbSize = 0;
1397
1398 /* Parse the command line. */
1399 static const RTGETOPTDEF s_aOptions[] =
1400 {
1401 { "--filename", 'f', RTGETOPT_REQ_STRING },
1402 { "--size", 's', RTGETOPT_REQ_UINT64 }
1403 };
1404 int ch;
1405 RTGETOPTUNION ValueUnion;
1406 RTGETOPTSTATE GetState;
1407 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1408 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1409 {
1410 switch (ch)
1411 {
1412 case 'f': // --filename
1413 pszFilename = ValueUnion.psz;
1414 break;
1415
1416 case 's': // --size
1417 cbSize = ValueUnion.u64;
1418 break;
1419
1420 default:
1421 ch = RTGetOptPrintError(ch, &ValueUnion);
1422 printUsage(g_pStdErr);
1423 return ch;
1424 }
1425 }
1426
1427 /* Check for mandatory parameters. */
1428 if (!pszFilename)
1429 return errorSyntax("Mandatory --filename option missing\n");
1430
1431 if (!cbSize)
1432 return errorSyntax("Mandatory --size option missing\n");
1433
1434 /* just try it */
1435 rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
1436 if (RT_FAILURE(rc))
1437 return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
1438
1439 rc = VDCreateCache(pDisk, "VCI", pszFilename, cbSize, VD_IMAGE_FLAGS_DEFAULT,
1440 NULL, NULL, VD_OPEN_FLAGS_NORMAL, NULL, NULL);
1441 if (RT_FAILURE(rc))
1442 return errorRuntime("Error while creating the virtual disk cache: %Rrc\n", rc);
1443
1444 VDDestroy(pDisk);
1445
1446 return rc;
1447}
1448
1449static DECLCALLBACK(bool) vdIfCfgCreateBaseAreKeysValid(void *pvUser, const char *pszzValid)
1450{
1451 return VINF_SUCCESS; /** @todo: Implement. */
1452}
1453
1454static DECLCALLBACK(int) vdIfCfgCreateBaseQuerySize(void *pvUser, const char *pszName, size_t *pcbValue)
1455{
1456 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
1457
1458 AssertPtrReturn(pvUser, VERR_GENERAL_FAILURE);
1459
1460 if (RTStrCmp(pszName, "DataAlignment"))
1461 return VERR_CFGM_VALUE_NOT_FOUND;
1462
1463 *pcbValue = strlen((const char *)pvUser) + 1 /* include terminator */;
1464
1465 return VINF_SUCCESS;
1466}
1467
1468static DECLCALLBACK(int) vdIfCfgCreateBaseQuery(void *pvUser, const char *pszName, char *pszValue, size_t cchValue)
1469{
1470 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
1471
1472 AssertPtrReturn(pvUser, VERR_GENERAL_FAILURE);
1473
1474 if (RTStrCmp(pszName, "DataAlignment"))
1475 return VERR_CFGM_VALUE_NOT_FOUND;
1476
1477 if (strlen((const char *)pvUser) >= cchValue)
1478 return VERR_CFGM_NOT_ENOUGH_SPACE;
1479
1480 memcpy(pszValue, pvUser, strlen((const char *)pvUser) + 1);
1481
1482 return VINF_SUCCESS;
1483
1484}
1485
1486static int handleCreateBase(HandlerArg *a)
1487{
1488 int rc = VINF_SUCCESS;
1489 PVBOXHDD pDisk = NULL;
1490 const char *pszFilename = NULL;
1491 const char *pszBackend = "VDI";
1492 const char *pszVariant = NULL;
1493 unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
1494 uint64_t cbSize = 0;
1495 const char *pszDataAlignment = NULL;
1496 VDGEOMETRY LCHSGeometry, PCHSGeometry;
1497 PVDINTERFACE pVDIfsOperation = NULL;
1498 VDINTERFACECONFIG vdIfCfg;
1499
1500 memset(&LCHSGeometry, 0, sizeof(LCHSGeometry));
1501 memset(&PCHSGeometry, 0, sizeof(PCHSGeometry));
1502
1503 /* Parse the command line. */
1504 static const RTGETOPTDEF s_aOptions[] =
1505 {
1506 { "--filename", 'f', RTGETOPT_REQ_STRING },
1507 { "--size", 's', RTGETOPT_REQ_UINT64 },
1508 { "--format", 'b', RTGETOPT_REQ_STRING },
1509 { "--variant", 'v', RTGETOPT_REQ_STRING },
1510 { "--dataalignment", 'a', RTGETOPT_REQ_STRING }
1511 };
1512 int ch;
1513 RTGETOPTUNION ValueUnion;
1514 RTGETOPTSTATE GetState;
1515 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1516 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1517 {
1518 switch (ch)
1519 {
1520 case 'f': // --filename
1521 pszFilename = ValueUnion.psz;
1522 break;
1523
1524 case 's': // --size
1525 cbSize = ValueUnion.u64;
1526 break;
1527
1528 case 'b': // --format
1529 pszBackend = ValueUnion.psz;
1530 break;
1531
1532 case 'v': // --variant
1533 pszVariant = ValueUnion.psz;
1534 break;
1535
1536 case 'a': // --dataalignment
1537 pszDataAlignment = ValueUnion.psz;
1538 break;
1539
1540 default:
1541 ch = RTGetOptPrintError(ch, &ValueUnion);
1542 printUsage(g_pStdErr);
1543 return ch;
1544 }
1545 }
1546
1547 /* Check for mandatory parameters. */
1548 if (!pszFilename)
1549 return errorSyntax("Mandatory --filename option missing\n");
1550
1551 if (!cbSize)
1552 return errorSyntax("Mandatory --size option missing\n");
1553
1554 if (pszVariant)
1555 {
1556 rc = parseDiskVariant(pszVariant, &uImageFlags);
1557 if (RT_FAILURE(rc))
1558 return errorSyntax("Invalid variant %s given\n", pszVariant);
1559 }
1560
1561 /* Setup the config interface if required. */
1562 if (pszDataAlignment)
1563 {
1564 vdIfCfg.pfnAreKeysValid = vdIfCfgCreateBaseAreKeysValid;
1565 vdIfCfg.pfnQuerySize = vdIfCfgCreateBaseQuerySize;
1566 vdIfCfg.pfnQuery = vdIfCfgCreateBaseQuery;
1567 VDInterfaceAdd(&vdIfCfg.Core, "Config", VDINTERFACETYPE_CONFIG, (void *)pszDataAlignment,
1568 sizeof(vdIfCfg), &pVDIfsOperation);
1569 }
1570
1571 /* just try it */
1572 rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
1573 if (RT_FAILURE(rc))
1574 return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
1575
1576 rc = VDCreateBase(pDisk, pszBackend, pszFilename, cbSize, uImageFlags,
1577 NULL, &PCHSGeometry, &LCHSGeometry, NULL, VD_OPEN_FLAGS_NORMAL,
1578 NULL, pVDIfsOperation);
1579 if (RT_FAILURE(rc))
1580 return errorRuntime("Error while creating the virtual disk: %Rrc\n", rc);
1581
1582 VDDestroy(pDisk);
1583
1584 return rc;
1585}
1586
1587
1588static int handleRepair(HandlerArg *a)
1589{
1590 int rc = VINF_SUCCESS;
1591 PVBOXHDD pDisk = NULL;
1592 const char *pszFilename = NULL;
1593 char *pszBackend = NULL;
1594 const char *pszFormat = NULL;
1595 bool fDryRun = false;
1596 VDTYPE enmType = VDTYPE_HDD;
1597
1598 /* Parse the command line. */
1599 static const RTGETOPTDEF s_aOptions[] =
1600 {
1601 { "--filename", 'f', RTGETOPT_REQ_STRING },
1602 { "--dry-run", 'd', RTGETOPT_REQ_NOTHING },
1603 { "--format", 'b', RTGETOPT_REQ_STRING }
1604 };
1605 int ch;
1606 RTGETOPTUNION ValueUnion;
1607 RTGETOPTSTATE GetState;
1608 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1609 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1610 {
1611 switch (ch)
1612 {
1613 case 'f': // --filename
1614 pszFilename = ValueUnion.psz;
1615 break;
1616
1617 case 'd': // --dry-run
1618 fDryRun = true;
1619 break;
1620
1621 case 'b': // --format
1622 pszFormat = ValueUnion.psz;
1623 break;
1624
1625 default:
1626 ch = RTGetOptPrintError(ch, &ValueUnion);
1627 printUsage(g_pStdErr);
1628 return ch;
1629 }
1630 }
1631
1632 /* Check for mandatory parameters. */
1633 if (!pszFilename)
1634 return errorSyntax("Mandatory --filename option missing\n");
1635
1636 /* just try it */
1637 if (!pszFormat)
1638 {
1639 rc = VDGetFormat(NULL, NULL, pszFilename, &pszBackend, &enmType);
1640 if (RT_FAILURE(rc))
1641 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
1642 pszFormat = pszBackend;
1643 }
1644
1645 rc = VDRepair(pVDIfs, NULL, pszFilename, pszFormat, fDryRun ? VD_REPAIR_DRY_RUN : 0);
1646 if (RT_FAILURE(rc))
1647 rc = errorRuntime("Error while repairing the virtual disk: %Rrc\n", rc);
1648
1649 if (pszBackend)
1650 RTStrFree(pszBackend);
1651 return rc;
1652}
1653
1654
1655static int handleClearComment(HandlerArg *a)
1656{
1657 int rc = VINF_SUCCESS;
1658 PVBOXHDD pDisk = NULL;
1659 const char *pszFilename = NULL;
1660 bool fDryRun = false;
1661
1662 /* Parse the command line. */
1663 static const RTGETOPTDEF s_aOptions[] =
1664 {
1665 { "--filename", 'f', RTGETOPT_REQ_STRING }
1666 };
1667 int ch;
1668 RTGETOPTUNION ValueUnion;
1669 RTGETOPTSTATE GetState;
1670 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1671 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1672 {
1673 switch (ch)
1674 {
1675 case 'f': // --filename
1676 pszFilename = ValueUnion.psz;
1677 break;
1678
1679 default:
1680 ch = RTGetOptPrintError(ch, &ValueUnion);
1681 printUsage(g_pStdErr);
1682 return ch;
1683 }
1684 }
1685
1686 /* Check for mandatory parameters. */
1687 if (!pszFilename)
1688 return errorSyntax("Mandatory --filename option missing\n");
1689
1690 /* just try it */
1691 char *pszFormat = NULL;
1692 VDTYPE enmType = VDTYPE_INVALID;
1693 rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat, &enmType);
1694 if (RT_FAILURE(rc))
1695 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
1696
1697 rc = VDCreate(pVDIfs, enmType, &pDisk);
1698 if (RT_FAILURE(rc))
1699 return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
1700
1701 /* Open the image */
1702 rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO, NULL);
1703 if (RT_FAILURE(rc))
1704 return errorRuntime("Error while opening the image: %Rrc\n", rc);
1705
1706 VDSetComment(pDisk, 0, NULL);
1707
1708 VDDestroy(pDisk);
1709 return rc;
1710}
1711
1712
1713static int handleClearResize(HandlerArg *a)
1714{
1715 int rc = VINF_SUCCESS;
1716 PVBOXHDD pDisk = NULL;
1717 const char *pszFilename = NULL;
1718 uint64_t cbNew = 0;
1719 bool fDryRun = false;
1720 VDGEOMETRY LCHSGeometry, PCHSGeometry;
1721
1722 memset(&LCHSGeometry, 0, sizeof(LCHSGeometry));
1723 memset(&PCHSGeometry, 0, sizeof(PCHSGeometry));
1724
1725 /* Parse the command line. */
1726 static const RTGETOPTDEF s_aOptions[] =
1727 {
1728 { "--filename", 'f', RTGETOPT_REQ_STRING },
1729 { "--size", 's', RTGETOPT_REQ_UINT64 }
1730 };
1731 int ch;
1732 RTGETOPTUNION ValueUnion;
1733 RTGETOPTSTATE GetState;
1734 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1735 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1736 {
1737 switch (ch)
1738 {
1739 case 'f': // --filename
1740 pszFilename = ValueUnion.psz;
1741 break;
1742
1743 case 's': // --size
1744 cbNew = ValueUnion.u64;
1745 break;
1746
1747 default:
1748 ch = RTGetOptPrintError(ch, &ValueUnion);
1749 printUsage(g_pStdErr);
1750 return ch;
1751 }
1752 }
1753
1754 /* Check for mandatory parameters. */
1755 if (!pszFilename)
1756 return errorSyntax("Mandatory --filename option missing\n");
1757
1758 if (!cbNew)
1759 return errorSyntax("Mandatory --size option missing or invalid\n");
1760
1761 /* just try it */
1762 char *pszFormat = NULL;
1763 VDTYPE enmType = VDTYPE_INVALID;
1764 rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat, &enmType);
1765 if (RT_FAILURE(rc))
1766 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
1767
1768 rc = VDCreate(pVDIfs, enmType, &pDisk);
1769 if (RT_FAILURE(rc))
1770 return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
1771
1772 /* Open the image */
1773 rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
1774 if (RT_FAILURE(rc))
1775 return errorRuntime("Error while opening the image: %Rrc\n", rc);
1776
1777 rc = VDResize(pDisk, cbNew, &PCHSGeometry, &LCHSGeometry, NULL);
1778 if (RT_FAILURE(rc))
1779 rc = errorRuntime("Error while resizing the virtual disk: %Rrc\n", rc);
1780
1781 VDDestroy(pDisk);
1782 return rc;
1783}
1784
1785
1786int main(int argc, char *argv[])
1787{
1788 int exitcode = 0;
1789
1790 int rc = RTR3InitExe(argc, &argv, 0);
1791 if (RT_FAILURE(rc))
1792 return RTMsgInitFailure(rc);
1793
1794 g_pszProgName = RTPathFilename(argv[0]);
1795
1796 bool fShowLogo = false;
1797 int iCmd = 1;
1798 int iCmdArg;
1799
1800 /* global options */
1801 for (int i = 1; i < argc || argc <= iCmd; i++)
1802 {
1803 if ( argc <= iCmd
1804 || !strcmp(argv[i], "help")
1805 || !strcmp(argv[i], "-?")
1806 || !strcmp(argv[i], "-h")
1807 || !strcmp(argv[i], "-help")
1808 || !strcmp(argv[i], "--help"))
1809 {
1810 showLogo(g_pStdOut);
1811 printUsage(g_pStdOut);
1812 return 0;
1813 }
1814
1815 if ( !strcmp(argv[i], "-v")
1816 || !strcmp(argv[i], "-version")
1817 || !strcmp(argv[i], "-Version")
1818 || !strcmp(argv[i], "--version"))
1819 {
1820 /* Print version number, and do nothing else. */
1821 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
1822 return 0;
1823 }
1824
1825 if ( !strcmp(argv[i], "--nologo")
1826 || !strcmp(argv[i], "-nologo")
1827 || !strcmp(argv[i], "-q"))
1828 {
1829 /* suppress the logo */
1830 fShowLogo = false;
1831 iCmd++;
1832 }
1833 else
1834 {
1835 break;
1836 }
1837 }
1838
1839 iCmdArg = iCmd + 1;
1840
1841 if (fShowLogo)
1842 showLogo(g_pStdOut);
1843
1844 /* initialize the VD backend with dummy handlers */
1845 VDINTERFACEERROR vdInterfaceError;
1846 vdInterfaceError.pfnError = handleVDError;
1847 vdInterfaceError.pfnMessage = handleVDMessage;
1848
1849 rc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1850 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
1851
1852 rc = VDInit();
1853 if (RT_FAILURE(rc))
1854 {
1855 errorSyntax("Initializing backends failed! rc=%Rrc\n", rc);
1856 return 1;
1857 }
1858
1859 /*
1860 * All registered command handlers
1861 */
1862 static const struct
1863 {
1864 const char *command;
1865 int (*handler)(HandlerArg *a);
1866 } s_commandHandlers[] =
1867 {
1868 { "setuuid", handleSetUUID },
1869 { "geometry", handleGeometry },
1870 { "convert", handleConvert },
1871 { "info", handleInfo },
1872 { "compact", handleCompact },
1873 { "createcache", handleCreateCache },
1874 { "createbase", handleCreateBase },
1875 { "repair", handleRepair },
1876 { "clearcomment", handleClearComment },
1877 { "resize", handleClearResize },
1878 { NULL, NULL }
1879 };
1880
1881 HandlerArg handlerArg = { 0, NULL };
1882 int commandIndex;
1883 for (commandIndex = 0; s_commandHandlers[commandIndex].command != NULL; commandIndex++)
1884 {
1885 if (!strcmp(s_commandHandlers[commandIndex].command, argv[iCmd]))
1886 {
1887 handlerArg.argc = argc - iCmdArg;
1888 handlerArg.argv = &argv[iCmdArg];
1889
1890 exitcode = s_commandHandlers[commandIndex].handler(&handlerArg);
1891 break;
1892 }
1893 }
1894 if (!s_commandHandlers[commandIndex].command)
1895 {
1896 errorSyntax("Invalid command '%s'", argv[iCmd]);
1897 return 1;
1898 }
1899
1900 rc = VDShutdown();
1901 if (RT_FAILURE(rc))
1902 {
1903 errorSyntax("Unloading backends failed! rc=%Rrc\n", rc);
1904 return 1;
1905 }
1906
1907 return exitcode;
1908}
1909
1910/* dummy stub for RuntimeR3 */
1911#ifndef RT_OS_WINDOWS
1912RTDECL(bool) RTAssertShouldPanic(void)
1913{
1914 return true;
1915}
1916#endif
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