VirtualBox

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

Last change on this file since 59710 was 59404, checked in by vboxsync, 9 years ago

Forward-ported r104938, r104943, r104950, r104952, r104953, r104987, r104988, r104990 from 5.0:

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