VirtualBox

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

Last change on this file since 66140 was 65812, checked in by vboxsync, 8 years ago

vbox-img: fixed 2 tiny memory leaks

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