VirtualBox

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

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

Storage/vbox-img: return 1 as exit code if anything went wrong

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.1 KB
Line 
1/* $Id: vbox-img.cpp 33227 2010-10-19 11:20:51Z vboxsync $ */
2/** @file
3 * Standalone image manipulation tool
4 */
5
6/*
7 * Copyright (C) 2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#include <VBox/VBoxHDD.h>
22#include <VBox/err.h>
23#include <VBox/version.h>
24#include <iprt/initterm.h>
25#include <iprt/buildconfig.h>
26#include <iprt/path.h>
27#include <iprt/string.h>
28#include <iprt/uuid.h>
29#include <iprt/stream.h>
30#include <iprt/message.h>
31#include <iprt/getopt.h>
32#include <iprt/assert.h>
33
34const char *g_pszProgName = "";
35static void printUsage(PRTSTREAM pStrm)
36{
37 RTStrmPrintf(pStrm,
38 "Usage: %s\n"
39 " setuuid --filename <filename>\n"
40 " [--format VDI|VMDK|VHD|...]\n"
41 " [--uuid <uuid>]\n"
42 " [--parentuuid <uuid>]\n"
43 " [--zeroparentuuid]\n"
44 "\n"
45 " convert --srcfilename <filename>\n"
46 " --dstfilename <filename>\n"
47 " [--stdin]|[--stdout]\n"
48 " [--srcformat VDI|VMDK|VHD|RAW|..]\n"
49 " [--dstformat VDI|VMDK|VHD|RAW|..]\n"
50 " [--variant Standard,Fixed,Split2G,Stream,ESX]\n"
51 "\n"
52 " info --filename <filename>\n"
53 "\n"
54 " compact --filename <filename>\n",
55 g_pszProgName);
56}
57
58void showLogo(PRTSTREAM pStrm)
59{
60 static bool s_fShown; /* show only once */
61
62 if (!s_fShown)
63 {
64 RTStrmPrintf(pStrm, VBOX_PRODUCT " Disk Utility " VBOX_VERSION_STRING "\n"
65 "(C) 2005-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
66 "All rights reserved.\n"
67 "\n");
68 s_fShown = true;
69 }
70}
71
72/** command handler argument */
73struct HandlerArg
74{
75 int argc;
76 char **argv;
77};
78
79PVDINTERFACE pVDIfs;
80
81static DECLCALLBACK(void) handleVDError(void *pvUser, int rc, RT_SRC_POS_DECL,
82 const char *pszFormat, va_list va)
83{
84 NOREF(pvUser);
85 NOREF(rc);
86 RTMsgErrorV(pszFormat, va);
87}
88
89static int handleVDMessage(void *pvUser, const char *pszFormat, va_list va)
90{
91 NOREF(pvUser);
92 RTPrintfV(pszFormat, va);
93 return VINF_SUCCESS;
94}
95
96/**
97 * Print a usage synopsis and the syntax error message.
98 */
99int errorSyntax(const char *pszFormat, ...)
100{
101 va_list args;
102 showLogo(g_pStdErr); // show logo even if suppressed
103 va_start(args, pszFormat);
104 RTStrmPrintf(g_pStdErr, "\nSyntax error: %N\n", pszFormat, &args);
105 va_end(args);
106 printUsage(g_pStdErr);
107 return 1;
108}
109
110int errorRuntime(const char *pszFormat, ...)
111{
112 va_list args;
113
114 va_start(args, pszFormat);
115 RTMsgErrorV(pszFormat, args);
116 va_end(args);
117 return 1;
118}
119
120
121
122int handleSetUUID(HandlerArg *a)
123{
124 const char *pszFilename = NULL;
125 char *pszFormat = NULL;
126 RTUUID imageUuid;
127 RTUUID parentUuid;
128 bool fSetImageUuid = false;
129 bool fSetParentUuid = false;
130 RTUuidClear(&imageUuid);
131 RTUuidClear(&parentUuid);
132 int rc;
133
134 /* Parse the command line. */
135 static const RTGETOPTDEF s_aOptions[] =
136 {
137 { "--filename", 'f', RTGETOPT_REQ_STRING },
138 { "--format", 'o', RTGETOPT_REQ_STRING },
139 { "--uuid", 'u', RTGETOPT_REQ_UUID },
140 { "--parentuuid", 'p', RTGETOPT_REQ_UUID },
141 { "--zeroparentuuid", 'P', RTGETOPT_REQ_NOTHING }
142 };
143 int ch;
144 RTGETOPTUNION ValueUnion;
145 RTGETOPTSTATE GetState;
146 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
147 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
148 {
149 switch (ch)
150 {
151 case 'f': // --filename
152 pszFilename = ValueUnion.psz;
153 break;
154 case 'o': // --format
155 pszFormat = RTStrDup(ValueUnion.psz);
156 break;
157 case 'u': // --uuid
158 imageUuid = ValueUnion.Uuid;
159 fSetImageUuid = true;
160 break;
161 case 'p': // --parentuuid
162 parentUuid = ValueUnion.Uuid;
163 fSetParentUuid = true;
164 break;
165 case 'P': // --zeroparentuuid
166 RTUuidClear(&parentUuid);
167 fSetParentUuid = true;
168 break;
169
170 default:
171 ch = RTGetOptPrintError(ch, &ValueUnion);
172 printUsage(g_pStdErr);
173 return ch;
174 }
175 }
176
177 /* Check for mandatory parameters. */
178 if (!pszFilename)
179 return errorSyntax("Mandatory --filename option missing\n");
180
181 /* Check for consistency of optional parameters. */
182 if (fSetImageUuid && RTUuidIsNull(&imageUuid))
183 return errorSyntax("Invalid parameter to --uuid option\n");
184
185 /* Autodetect image format. */
186 if (!pszFormat)
187 {
188 /* Don't pass error interface, as that would triggers error messages
189 * because some backends fail to open the image. */
190 rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat);
191 if (RT_FAILURE(rc))
192 return errorRuntime("Format autodetect failed: %Rrc\n", rc);
193 }
194
195 PVBOXHDD pVD = NULL;
196 rc = VDCreate(pVDIfs, &pVD);
197 if (RT_FAILURE(rc))
198 return errorRuntime("Cannot create the virtual disk container: %Rrc\n", rc);
199
200
201 rc = VDOpen(pVD, pszFormat, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
202 if (RT_FAILURE(rc))
203 return errorRuntime("Cannot open the virtual disk image \"%s\": %Rrc\n",
204 pszFilename, rc);
205
206 RTUUID oldImageUuid;
207 rc = VDGetUuid(pVD, VD_LAST_IMAGE, &oldImageUuid);
208 if (RT_FAILURE(rc))
209 return errorRuntime("Cannot get UUID of virtual disk image \"%s\": %Rrc\n",
210 pszFilename, rc);
211
212 RTPrintf("Old image UUID: %RTuuid\n", &oldImageUuid);
213
214 RTUUID oldParentUuid;
215 rc = VDGetParentUuid(pVD, VD_LAST_IMAGE, &oldParentUuid);
216 if (RT_FAILURE(rc))
217 return errorRuntime("Cannot get parent UUID of virtual disk image \"%s\": %Rrc\n",
218 pszFilename, rc);
219
220 RTPrintf("Old parent UUID: %RTuuid\n", &oldParentUuid);
221
222 if (fSetImageUuid)
223 {
224 RTPrintf("New image UUID: %RTuuid\n", &imageUuid);
225 rc = VDSetUuid(pVD, VD_LAST_IMAGE, &imageUuid);
226 if (RT_FAILURE(rc))
227 return errorRuntime("Cannot set UUID of virtual disk image \"%s\": %Rrc\n",
228 pszFilename, rc);
229 }
230
231 if (fSetParentUuid)
232 {
233 RTPrintf("New parent UUID: %RTuuid\n", &parentUuid);
234 rc = VDSetParentUuid(pVD, VD_LAST_IMAGE, &parentUuid);
235 if (RT_FAILURE(rc))
236 return errorRuntime("Cannot set parent UUID of virtual disk image \"%s\": %Rrc\n",
237 pszFilename, rc);
238 }
239
240 rc = VDCloseAll(pVD);
241 if (RT_FAILURE(rc))
242 return errorRuntime("Closing image failed! rc=%Rrc\n", rc);
243
244 if (pszFormat)
245 {
246 RTStrFree(pszFormat);
247 pszFormat = NULL;
248 }
249
250 return 0;
251}
252
253
254typedef struct FILEIOSTATE
255{
256 /** Offset in the file. */
257 uint64_t off;
258 /** Offset where the buffer contents start. UINT64_MAX=buffer invalid. */
259 uint64_t offBuffer;
260 /** Size of valid data in the buffer. */
261 uint32_t cbBuffer;
262 /** Buffer for efficient I/O */
263 uint8_t abBuffer[16 *_1M];
264} FILEIOSTATE, *PFILEIOSTATE;
265
266static int convInOpen(void *pvUser, const char *pszLocation,
267 uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
268 void **ppStorage)
269{
270 NOREF(pvUser);
271 /* Validate input. */
272 AssertPtrReturn(ppStorage, VERR_INVALID_POINTER);
273 AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
274 AssertReturn((fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ, VERR_INVALID_PARAMETER);
275
276 PFILEIOSTATE pFS = (PFILEIOSTATE)RTMemAlloc(sizeof(FILEIOSTATE));
277 if (!pFS)
278 return VERR_NO_MEMORY;
279
280 pFS->off = 0;
281 pFS->offBuffer = UINT64_MAX;
282 pFS->cbBuffer = 0;
283
284 *ppStorage = pFS;
285 return VINF_SUCCESS;
286}
287
288static int convInClose(void *pvUser, void *pStorage)
289{
290 NOREF(pvUser);
291 AssertPtrReturn(pStorage, VERR_INVALID_POINTER);
292
293 PFILEIOSTATE pFS = (PFILEIOSTATE)pStorage;
294
295 RTMemFree(pFS);
296
297 return VINF_SUCCESS;
298}
299
300static int convInDelete(void *pvUser, const char *pcszFilename)
301{
302 NOREF(pvUser);
303 NOREF(pcszFilename);
304 AssertFailedReturn(VERR_NOT_SUPPORTED);
305}
306
307static int convInMove(void *pvUser, const char *pcszSrc, const char *pcszDst,
308 unsigned fMove)
309{
310 NOREF(pvUser);
311 NOREF(pcszSrc);
312 NOREF(pcszDst);
313 NOREF(fMove);
314 AssertFailedReturn(VERR_NOT_SUPPORTED);
315}
316
317static int convInGetFreeSpace(void *pvUser, const char *pcszFilename,
318 int64_t *pcbFreeSpace)
319{
320 NOREF(pvUser);
321 NOREF(pcszFilename);
322 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
323 *pcbFreeSpace = 0;
324 return VINF_SUCCESS;
325}
326
327static int convInGetModificationTime(void *pvUser, const char *pcszFilename,
328 PRTTIMESPEC pModificationTime)
329{
330 NOREF(pvUser);
331 NOREF(pcszFilename);
332 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
333 AssertFailedReturn(VERR_NOT_SUPPORTED);
334}
335
336static int convInGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
337{
338 NOREF(pvUser);
339 NOREF(pStorage);
340 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
341 AssertFailedReturn(VERR_NOT_SUPPORTED);
342}
343
344static int convInSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
345{
346 NOREF(pvUser);
347 NOREF(pStorage);
348 NOREF(cbSize);
349 AssertFailedReturn(VERR_NOT_SUPPORTED);
350}
351
352static int convInRead(void *pvUser, void *pStorage, uint64_t uOffset,
353 void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
354{
355 NOREF(pvUser);
356 AssertPtrReturn(pStorage, VERR_INVALID_POINTER);
357 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
358 PFILEIOSTATE pFS = (PFILEIOSTATE)pStorage;
359 AssertReturn(uOffset >= pFS->off, VERR_INVALID_PARAMETER);
360 int rc;
361
362 /* Fill buffer if it is empty. */
363 if (pFS->offBuffer == UINT64_MAX)
364 {
365 /* Repeat reading until buffer is full or EOF. */
366 size_t cbSumRead = 0, cbRead;
367 uint8_t *pTmp = (uint8_t *)&pFS->abBuffer[0];
368 size_t cbTmp = sizeof(pFS->abBuffer);
369 do
370 {
371 rc = RTFileRead(0, pTmp, cbTmp, &cbRead);
372 if (RT_FAILURE(rc))
373 return rc;
374 pTmp += cbRead;
375 cbTmp -= cbRead;
376 cbSumRead += cbRead;
377 } while (cbTmp && cbRead);
378
379 pFS->offBuffer = 0;
380 pFS->cbBuffer = cbSumRead;
381 }
382
383 /* Read several blocks and assemble the result if necessary */
384 size_t cbTotalRead = 0;
385 do
386 {
387 /* Skip over areas no one wants to read. */
388 while (uOffset > pFS->offBuffer + pFS->cbBuffer - 1)
389 {
390 if (pFS->cbBuffer < sizeof(pFS->abBuffer))
391 {
392 if (pcbRead)
393 *pcbRead = cbTotalRead;
394 return VERR_EOF;
395 }
396
397 /* Repeat reading until buffer is full or EOF. */
398 size_t cbSumRead = 0, cbRead;
399 uint8_t *pTmp = (uint8_t *)&pFS->abBuffer[0];
400 size_t cbTmp = sizeof(pFS->abBuffer);
401 do
402 {
403 rc = RTFileRead(0, pTmp, cbTmp, &cbRead);
404 if (RT_FAILURE(rc))
405 return rc;
406 pTmp += cbRead;
407 cbTmp -= cbRead;
408 cbSumRead += cbRead;
409 } while (cbTmp && cbRead);
410
411 pFS->offBuffer += pFS->cbBuffer;
412 pFS->cbBuffer = cbSumRead;
413 }
414
415 uint32_t cbThisRead = RT_MIN(cbBuffer,
416 pFS->cbBuffer - uOffset % sizeof(pFS->abBuffer));
417 memcpy(pvBuffer, &pFS->abBuffer[uOffset % sizeof(pFS->abBuffer)],
418 cbThisRead);
419 uOffset += cbThisRead;
420 pvBuffer = (uint8_t *)pvBuffer + cbThisRead;
421 cbBuffer -= cbThisRead;
422 cbTotalRead += cbThisRead;
423 } while (cbBuffer > 0);
424
425 if (pcbRead)
426 *pcbRead = cbTotalRead;
427
428 pFS->off = uOffset;
429
430 return VINF_SUCCESS;
431}
432
433static int convInWrite(void *pvUser, void *pStorage, uint64_t uOffset,
434 const void *pvBuffer, size_t cbBuffer,
435 size_t *pcbWritten)
436{
437 NOREF(pvUser);
438 NOREF(pStorage);
439 NOREF(uOffset);
440 NOREF(cbBuffer);
441 NOREF(pcbWritten);
442 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
443 AssertFailedReturn(VERR_NOT_SUPPORTED);
444}
445
446static int convInFlush(void *pvUser, void *pStorage)
447{
448 NOREF(pvUser);
449 NOREF(pStorage);
450 return VINF_SUCCESS;
451}
452
453static int convOutOpen(void *pvUser, const char *pszLocation,
454 uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
455 void **ppStorage)
456{
457 NOREF(pvUser);
458 /* Validate input. */
459 AssertPtrReturn(ppStorage, VERR_INVALID_POINTER);
460 AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
461 AssertReturn((fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_WRITE, VERR_INVALID_PARAMETER);
462
463 PFILEIOSTATE pFS = (PFILEIOSTATE)RTMemAlloc(sizeof(FILEIOSTATE));
464 if (!pFS)
465 return VERR_NO_MEMORY;
466
467 pFS->off = 0;
468 pFS->offBuffer = UINT64_MAX;
469 pFS->cbBuffer = 0;
470
471 *ppStorage = pFS;
472 return VINF_SUCCESS;
473}
474
475static int convOutClose(void *pvUser, void *pStorage)
476{
477 NOREF(pvUser);
478 AssertPtrReturn(pStorage, VERR_INVALID_POINTER);
479
480 PFILEIOSTATE pFS = (PFILEIOSTATE)pStorage;
481
482 RTMemFree(pFS);
483
484 return VINF_SUCCESS;
485}
486
487static int convOutDelete(void *pvUser, const char *pcszFilename)
488{
489 NOREF(pvUser);
490 NOREF(pcszFilename);
491 AssertFailedReturn(VERR_NOT_SUPPORTED);
492}
493
494static int convOutMove(void *pvUser, const char *pcszSrc, const char *pcszDst,
495 unsigned fMove)
496{
497 NOREF(pvUser);
498 NOREF(pcszSrc);
499 NOREF(pcszDst);
500 NOREF(fMove);
501 AssertFailedReturn(VERR_NOT_SUPPORTED);
502}
503
504static int convOutGetFreeSpace(void *pvUser, const char *pcszFilename,
505 int64_t *pcbFreeSpace)
506{
507 NOREF(pvUser);
508 NOREF(pcszFilename);
509 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
510 *pcbFreeSpace = INT64_MAX;
511 return VINF_SUCCESS;
512}
513
514static int convOutGetModificationTime(void *pvUser, const char *pcszFilename,
515 PRTTIMESPEC pModificationTime)
516{
517 NOREF(pvUser);
518 NOREF(pcszFilename);
519 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
520 AssertFailedReturn(VERR_NOT_SUPPORTED);
521}
522
523static int convOutGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
524{
525 NOREF(pvUser);
526 NOREF(pStorage);
527 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
528 AssertFailedReturn(VERR_NOT_SUPPORTED);
529}
530
531static int convOutSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
532{
533 NOREF(pvUser);
534 NOREF(pStorage);
535 NOREF(cbSize);
536 AssertFailedReturn(VERR_NOT_SUPPORTED);
537}
538
539static int convOutRead(void *pvUser, void *pStorage, uint64_t uOffset,
540 void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
541{
542 NOREF(pvUser);
543 NOREF(pStorage);
544 NOREF(uOffset);
545 NOREF(cbBuffer);
546 NOREF(pcbRead);
547 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
548 AssertFailedReturn(VERR_NOT_SUPPORTED);
549}
550
551static int convOutWrite(void *pvUser, void *pStorage, uint64_t uOffset,
552 const void *pvBuffer, size_t cbBuffer,
553 size_t *pcbWritten)
554{
555 NOREF(pvUser);
556 NOREF(pStorage);
557 NOREF(uOffset);
558 NOREF(cbBuffer);
559 NOREF(pcbWritten);
560 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
561 AssertFailedReturn(VERR_NOT_SUPPORTED);
562}
563
564static int convOutFlush(void *pvUser, void *pStorage)
565{
566 NOREF(pvUser);
567 NOREF(pStorage);
568 return VINF_SUCCESS;
569}
570
571int handleConvert(HandlerArg *a)
572{
573 const char *pszSrcFilename = NULL;
574 const char *pszDstFilename = NULL;
575 bool fStdIn = false;
576 bool fStdOut = false;
577 char *pszSrcFormat = NULL;
578 const char *pszDstFormat = NULL;
579 const char *pszVariant = NULL;
580 PVBOXHDD pSrcDisk = NULL;
581 PVBOXHDD pDstDisk = NULL;
582 unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
583 PVDINTERFACE pIfsImageInput = NULL;
584 PVDINTERFACE pIfsImageOutput = NULL;
585 VDINTERFACE IfsInputIO;
586 VDINTERFACE IfsOutputIO;
587 VDINTERFACEIO IfsInputIOCb;
588 VDINTERFACEIO IfsOutputIOCb;
589 int rc = VINF_SUCCESS;
590
591 /* Parse the command line. */
592 static const RTGETOPTDEF s_aOptions[] =
593 {
594 { "--srcfilename", 'i', RTGETOPT_REQ_STRING },
595 { "--dstfilename", 'o', RTGETOPT_REQ_STRING },
596 { "--stdin", 'p', RTGETOPT_REQ_NOTHING },
597 { "--stdout", 'P', RTGETOPT_REQ_NOTHING },
598 { "--srcformat", 's', RTGETOPT_REQ_STRING },
599 { "--dstformat", 'd', RTGETOPT_REQ_STRING },
600 { "--variant", 'v', RTGETOPT_REQ_STRING }
601 };
602 int ch;
603 RTGETOPTUNION ValueUnion;
604 RTGETOPTSTATE GetState;
605 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
606 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
607 {
608 switch (ch)
609 {
610 case 'i': // --srcfilename
611 pszSrcFilename = ValueUnion.psz;
612 break;
613 case 'o': // --dstfilename
614 pszDstFilename = ValueUnion.psz;
615 break;
616 case 'p': // --stdin
617 fStdIn = true;
618 break;
619 case 'P': // --stdout
620 fStdOut = true;
621 break;
622 case 's': // --srcformat
623 pszSrcFormat = RTStrDup(ValueUnion.psz);
624 break;
625 case 'd': // --dstformat
626 pszDstFormat = RTStrDup(ValueUnion.psz);
627 break;
628 case 'v': // --variant
629 pszVariant = ValueUnion.psz;
630 break;
631
632 default:
633 ch = RTGetOptPrintError(ch, &ValueUnion);
634 printUsage(g_pStdErr);
635 return ch;
636 }
637 }
638
639 /* Check for mandatory parameters. */
640 if (!pszSrcFilename)
641 return errorSyntax("Mandatory --srcfilename option missing\n");
642 if (!pszDstFilename)
643 return errorSyntax("Mandatory --dstfilename option missing\n");
644
645 if (fStdIn)
646 {
647 IfsInputIOCb.cbSize = sizeof(VDINTERFACEIO);
648 IfsInputIOCb.enmInterface = VDINTERFACETYPE_IO;
649 IfsInputIOCb.pfnOpen = convInOpen;
650 IfsInputIOCb.pfnClose = convInClose;
651 IfsInputIOCb.pfnDelete = convInDelete;
652 IfsInputIOCb.pfnMove = convInMove;
653 IfsInputIOCb.pfnGetFreeSpace = convInGetFreeSpace;
654 IfsInputIOCb.pfnGetModificationTime = convInGetModificationTime;
655 IfsInputIOCb.pfnGetSize = convInGetSize;
656 IfsInputIOCb.pfnSetSize = convInSetSize;
657 IfsInputIOCb.pfnReadSync = convInRead;
658 IfsInputIOCb.pfnWriteSync = convInWrite;
659 IfsInputIOCb.pfnFlushSync = convInFlush;
660 VDInterfaceAdd(&IfsInputIO, "stdin", VDINTERFACETYPE_IO,
661 &IfsInputIOCb, NULL, &pIfsImageInput);
662 }
663 if (fStdOut)
664 {
665 IfsOutputIOCb.cbSize = sizeof(VDINTERFACEIO);
666 IfsOutputIOCb.enmInterface = VDINTERFACETYPE_IO;
667 IfsOutputIOCb.pfnOpen = convOutOpen;
668 IfsOutputIOCb.pfnClose = convOutClose;
669 IfsOutputIOCb.pfnDelete = convOutDelete;
670 IfsOutputIOCb.pfnMove = convOutMove;
671 IfsOutputIOCb.pfnGetFreeSpace = convOutGetFreeSpace;
672 IfsOutputIOCb.pfnGetModificationTime = convOutGetModificationTime;
673 IfsOutputIOCb.pfnGetSize = convOutGetSize;
674 IfsOutputIOCb.pfnSetSize = convOutSetSize;
675 IfsOutputIOCb.pfnReadSync = convOutRead;
676 IfsOutputIOCb.pfnWriteSync = convOutWrite;
677 IfsOutputIOCb.pfnFlushSync = convOutFlush;
678 VDInterfaceAdd(&IfsOutputIO, "stdout", VDINTERFACETYPE_IO,
679 &IfsOutputIOCb, NULL, &pIfsImageOutput);
680 }
681
682 /* check the variant parameter */
683 if (pszVariant)
684 {
685 char *psz = (char*)pszVariant;
686 while (psz && *psz && RT_SUCCESS(rc))
687 {
688 size_t len;
689 const char *pszComma = strchr(psz, ',');
690 if (pszComma)
691 len = pszComma - psz;
692 else
693 len = strlen(psz);
694 if (len > 0)
695 {
696 if (!RTStrNICmp(pszVariant, "standard", len))
697 uImageFlags |= VD_IMAGE_FLAGS_NONE;
698 else if (!RTStrNICmp(pszVariant, "fixed", len))
699 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
700 else if (!RTStrNICmp(pszVariant, "split2g", len))
701 uImageFlags |= VD_VMDK_IMAGE_FLAGS_SPLIT_2G;
702 else if (!RTStrNICmp(pszVariant, "stream", len))
703 uImageFlags |= VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED;
704 else if (!RTStrNICmp(pszVariant, "esx", len))
705 uImageFlags |= VD_VMDK_IMAGE_FLAGS_ESX;
706 else
707 return errorSyntax("Invalid --variant option\n");
708 }
709 if (pszComma)
710 psz += len + 1;
711 else
712 psz += len;
713 }
714 }
715
716 do
717 {
718 /* try to determine input format if not specified */
719 if (!pszSrcFormat)
720 {
721 rc = VDGetFormat(NULL, NULL, pszSrcFilename, &pszSrcFormat);
722 if (RT_FAILURE(rc))
723 {
724 errorSyntax("No file format specified, please specify format: %Rrc\n", rc);
725 break;
726 }
727 }
728
729 rc = VDCreate(pVDIfs, &pSrcDisk);
730 if (RT_FAILURE(rc))
731 {
732 errorRuntime("Error while creating source disk container: %Rrc\n", rc);
733 break;
734 }
735
736 rc = VDOpen(pSrcDisk, pszSrcFormat, pszSrcFilename,
737 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL,
738 pIfsImageInput);
739 if (RT_FAILURE(rc))
740 {
741 errorRuntime("Error while opening source image: %Rrc\n", rc);
742 break;
743 }
744
745 /* output format defaults to VDI */
746 if (!pszDstFormat)
747 pszDstFormat = "VDI";
748
749 rc = VDCreate(pVDIfs, &pDstDisk);
750 if (RT_FAILURE(rc))
751 {
752 errorRuntime("Error while creating the destination disk container: %Rrc\n", rc);
753 break;
754 }
755
756 uint64_t cbSize = VDGetSize(pSrcDisk, VD_LAST_IMAGE);
757 RTStrmPrintf(g_pStdErr, "Converting image \"%s\" with size %RU64 bytes (%RU64MB)...\n", pszSrcFilename, cbSize, (cbSize + _1M - 1) / _1M);
758
759 /* Create the output image */
760 rc = VDCopy(pSrcDisk, VD_LAST_IMAGE, pDstDisk, pszDstFormat,
761 pszDstFilename, false, 0, uImageFlags, NULL,
762 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL, NULL,
763 pIfsImageOutput, NULL);
764 if (RT_FAILURE(rc))
765 {
766 errorRuntime("Error while copying the image: %Rrc\n", rc);
767 break;
768 }
769
770 }
771 while (0);
772
773 if (pDstDisk)
774 VDCloseAll(pDstDisk);
775 if (pSrcDisk)
776 VDCloseAll(pSrcDisk);
777
778 return RT_SUCCESS(rc) ? 0 : 1;
779}
780
781
782int handleInfo(HandlerArg *a)
783{
784 int rc = VINF_SUCCESS;
785 PVBOXHDD pDisk = NULL;
786 const char *pszFilename = NULL;
787
788 /* Parse the command line. */
789 static const RTGETOPTDEF s_aOptions[] =
790 {
791 { "--filename", 'f', RTGETOPT_REQ_STRING }
792 };
793 int ch;
794 RTGETOPTUNION ValueUnion;
795 RTGETOPTSTATE GetState;
796 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
797 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
798 {
799 switch (ch)
800 {
801 case 'f': // --filename
802 pszFilename = ValueUnion.psz;
803 break;
804
805 default:
806 ch = RTGetOptPrintError(ch, &ValueUnion);
807 printUsage(g_pStdErr);
808 return ch;
809 }
810 }
811
812 /* Check for mandatory parameters. */
813 if (!pszFilename)
814 return errorSyntax("Mandatory --filename option missing\n");
815
816 /* just try it */
817 char *pszFormat = NULL;
818 rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat);
819 if (RT_FAILURE(rc))
820 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
821
822 rc = VDCreate(pVDIfs, &pDisk);
823 if (RT_FAILURE(rc))
824 return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
825
826 /* Open the image */
827 rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO, NULL);
828 if (RT_FAILURE(rc))
829 return errorRuntime("Error while opening the image: %Rrc\n", rc);
830
831 VDDumpImages(pDisk);
832
833 VDCloseAll(pDisk);
834
835 return rc;
836}
837
838
839int handleCompact(HandlerArg *a)
840{
841 int rc = VINF_SUCCESS;
842 PVBOXHDD pDisk = NULL;
843 const char *pszFilename = NULL;
844
845 /* Parse the command line. */
846 static const RTGETOPTDEF s_aOptions[] =
847 {
848 { "--filename", 'f', RTGETOPT_REQ_STRING }
849 };
850 int ch;
851 RTGETOPTUNION ValueUnion;
852 RTGETOPTSTATE GetState;
853 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
854 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
855 {
856 switch (ch)
857 {
858 case 'f': // --filename
859 pszFilename = ValueUnion.psz;
860 break;
861
862 default:
863 ch = RTGetOptPrintError(ch, &ValueUnion);
864 printUsage(g_pStdErr);
865 return ch;
866 }
867 }
868
869 /* Check for mandatory parameters. */
870 if (!pszFilename)
871 return errorSyntax("Mandatory --filename option missing\n");
872
873 /* just try it */
874 char *pszFormat = NULL;
875 rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat);
876 if (RT_FAILURE(rc))
877 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
878
879 rc = VDCreate(pVDIfs, &pDisk);
880 if (RT_FAILURE(rc))
881 return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
882
883 /* Open the image */
884 rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
885 if (RT_FAILURE(rc))
886 return errorRuntime("Error while opening the image: %Rrc\n", rc);
887
888 rc = VDCompact(pDisk, 0, NULL);
889 if (RT_FAILURE(rc))
890 errorRuntime("Error while compacting image: %Rrc\n", rc);
891
892 VDCloseAll(pDisk);
893
894 return rc;
895}
896
897
898int main(int argc, char *argv[])
899{
900 RTR3Init();
901 int rc;
902 int exitcode = 0;
903
904 g_pszProgName = RTPathFilename(argv[0]);
905
906 bool fShowLogo = false;
907 int iCmd = 1;
908 int iCmdArg;
909
910 /* global options */
911 for (int i = 1; i < argc || argc <= iCmd; i++)
912 {
913 if ( argc <= iCmd
914 || !strcmp(argv[i], "help")
915 || !strcmp(argv[i], "-?")
916 || !strcmp(argv[i], "-h")
917 || !strcmp(argv[i], "-help")
918 || !strcmp(argv[i], "--help"))
919 {
920 showLogo(g_pStdOut);
921 printUsage(g_pStdOut);
922 return 0;
923 }
924
925 if ( !strcmp(argv[i], "-v")
926 || !strcmp(argv[i], "-version")
927 || !strcmp(argv[i], "-Version")
928 || !strcmp(argv[i], "--version"))
929 {
930 /* Print version number, and do nothing else. */
931 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
932 return 0;
933 }
934
935 if ( !strcmp(argv[i], "--nologo")
936 || !strcmp(argv[i], "-nologo")
937 || !strcmp(argv[i], "-q"))
938 {
939 /* suppress the logo */
940 fShowLogo = false;
941 iCmd++;
942 }
943 else
944 {
945 break;
946 }
947 }
948
949 iCmdArg = iCmd + 1;
950
951 if (fShowLogo)
952 showLogo(g_pStdOut);
953
954 /* initialize the VD backend with dummy handlers */
955 VDINTERFACE vdInterfaceError;
956 VDINTERFACEERROR vdInterfaceErrorCallbacks;
957 vdInterfaceErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
958 vdInterfaceErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
959 vdInterfaceErrorCallbacks.pfnError = handleVDError;
960 vdInterfaceErrorCallbacks.pfnMessage = handleVDMessage;
961
962 rc = VDInterfaceAdd(&vdInterfaceError, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
963 &vdInterfaceErrorCallbacks, NULL, &pVDIfs);
964
965 rc = VDInit();
966 if (RT_FAILURE(rc))
967 {
968 errorSyntax("Initalizing backends failed! rc=%Rrc\n", rc);
969 return 1;
970 }
971
972 /*
973 * All registered command handlers
974 */
975 static const struct
976 {
977 const char *command;
978 int (*handler)(HandlerArg *a);
979 } s_commandHandlers[] =
980 {
981 { "setuuid", handleSetUUID },
982 { "convert", handleConvert },
983 { "info", handleInfo },
984 { "compact", handleCompact },
985 { NULL, NULL }
986 };
987
988 HandlerArg handlerArg = { 0, NULL };
989 int commandIndex;
990 for (commandIndex = 0; s_commandHandlers[commandIndex].command != NULL; commandIndex++)
991 {
992 if (!strcmp(s_commandHandlers[commandIndex].command, argv[iCmd]))
993 {
994 handlerArg.argc = argc - iCmdArg;
995 handlerArg.argv = &argv[iCmdArg];
996
997 exitcode = s_commandHandlers[commandIndex].handler(&handlerArg);
998 break;
999 }
1000 }
1001 if (!s_commandHandlers[commandIndex].command)
1002 {
1003 errorSyntax("Invalid command '%s'", argv[iCmd]);
1004 return 1;
1005 }
1006
1007 rc = VDShutdown();
1008 if (RT_FAILURE(rc))
1009 {
1010 errorSyntax("Unloading backends failed! rc=%Rrc\n", rc);
1011 return 1;
1012 }
1013
1014 return exitcode;
1015}
1016
1017/* dummy stub for RuntimeR3 */
1018#ifndef RT_OS_WINDOWS
1019RTDECL(bool) RTAssertShouldPanic(void)
1020{
1021 return true;
1022}
1023#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