VirtualBox

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

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

Storage: Implement offical support for other disk types like DVD and floppy images. DMG images can be used now without hacks

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 33.0 KB
Line 
1/* $Id: vbox-img.cpp 33524 2010-10-27 16:44:37Z 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 VDTYPE enmType = VDTYPE_INVALID;
127 RTUUID imageUuid;
128 RTUUID parentUuid;
129 bool fSetImageUuid = false;
130 bool fSetParentUuid = false;
131 RTUuidClear(&imageUuid);
132 RTUuidClear(&parentUuid);
133 int rc;
134
135 /* Parse the command line. */
136 static const RTGETOPTDEF s_aOptions[] =
137 {
138 { "--filename", 'f', RTGETOPT_REQ_STRING },
139 { "--format", 'o', RTGETOPT_REQ_STRING },
140 { "--uuid", 'u', RTGETOPT_REQ_UUID },
141 { "--parentuuid", 'p', RTGETOPT_REQ_UUID },
142 { "--zeroparentuuid", 'P', RTGETOPT_REQ_NOTHING }
143 };
144 int ch;
145 RTGETOPTUNION ValueUnion;
146 RTGETOPTSTATE GetState;
147 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
148 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
149 {
150 switch (ch)
151 {
152 case 'f': // --filename
153 pszFilename = ValueUnion.psz;
154 break;
155 case 'o': // --format
156 pszFormat = RTStrDup(ValueUnion.psz);
157 break;
158 case 'u': // --uuid
159 imageUuid = ValueUnion.Uuid;
160 fSetImageUuid = true;
161 break;
162 case 'p': // --parentuuid
163 parentUuid = ValueUnion.Uuid;
164 fSetParentUuid = true;
165 break;
166 case 'P': // --zeroparentuuid
167 RTUuidClear(&parentUuid);
168 fSetParentUuid = true;
169 break;
170
171 default:
172 ch = RTGetOptPrintError(ch, &ValueUnion);
173 printUsage(g_pStdErr);
174 return ch;
175 }
176 }
177
178 /* Check for mandatory parameters. */
179 if (!pszFilename)
180 return errorSyntax("Mandatory --filename option missing\n");
181
182 /* Check for consistency of optional parameters. */
183 if (fSetImageUuid && RTUuidIsNull(&imageUuid))
184 return errorSyntax("Invalid parameter to --uuid option\n");
185
186 /* Autodetect image format. */
187 if (!pszFormat)
188 {
189 /* Don't pass error interface, as that would triggers error messages
190 * because some backends fail to open the image. */
191 rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat, &enmType);
192 if (RT_FAILURE(rc))
193 return errorRuntime("Format autodetect failed: %Rrc\n", rc);
194 }
195
196 PVBOXHDD pVD = NULL;
197 rc = VDCreate(pVDIfs, enmType, &pVD);
198 if (RT_FAILURE(rc))
199 return errorRuntime("Cannot create the virtual disk container: %Rrc\n", rc);
200
201
202 rc = VDOpen(pVD, pszFormat, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
203 if (RT_FAILURE(rc))
204 return errorRuntime("Cannot open the virtual disk image \"%s\": %Rrc\n",
205 pszFilename, rc);
206
207 RTUUID oldImageUuid;
208 rc = VDGetUuid(pVD, VD_LAST_IMAGE, &oldImageUuid);
209 if (RT_FAILURE(rc))
210 return errorRuntime("Cannot get UUID of virtual disk image \"%s\": %Rrc\n",
211 pszFilename, rc);
212
213 RTPrintf("Old image UUID: %RTuuid\n", &oldImageUuid);
214
215 RTUUID oldParentUuid;
216 rc = VDGetParentUuid(pVD, VD_LAST_IMAGE, &oldParentUuid);
217 if (RT_FAILURE(rc))
218 return errorRuntime("Cannot get parent UUID of virtual disk image \"%s\": %Rrc\n",
219 pszFilename, rc);
220
221 RTPrintf("Old parent UUID: %RTuuid\n", &oldParentUuid);
222
223 if (fSetImageUuid)
224 {
225 RTPrintf("New image UUID: %RTuuid\n", &imageUuid);
226 rc = VDSetUuid(pVD, VD_LAST_IMAGE, &imageUuid);
227 if (RT_FAILURE(rc))
228 return errorRuntime("Cannot set UUID of virtual disk image \"%s\": %Rrc\n",
229 pszFilename, rc);
230 }
231
232 if (fSetParentUuid)
233 {
234 RTPrintf("New parent UUID: %RTuuid\n", &parentUuid);
235 rc = VDSetParentUuid(pVD, VD_LAST_IMAGE, &parentUuid);
236 if (RT_FAILURE(rc))
237 return errorRuntime("Cannot set parent UUID of virtual disk image \"%s\": %Rrc\n",
238 pszFilename, rc);
239 }
240
241 rc = VDCloseAll(pVD);
242 if (RT_FAILURE(rc))
243 return errorRuntime("Closing image failed! rc=%Rrc\n", rc);
244
245 if (pszFormat)
246 {
247 RTStrFree(pszFormat);
248 pszFormat = NULL;
249 }
250
251 return 0;
252}
253
254
255typedef struct FILEIOSTATE
256{
257 RTFILE file;
258 /** Offset in the file. */
259 uint64_t off;
260 /** Offset where the buffer contents start. UINT64_MAX=buffer invalid. */
261 uint64_t offBuffer;
262 /** Size of valid data in the buffer. */
263 uint32_t cbBuffer;
264 /** Buffer for efficient I/O */
265 uint8_t abBuffer[16 *_1M];
266} FILEIOSTATE, *PFILEIOSTATE;
267
268static int convInOpen(void *pvUser, const char *pszLocation,
269 uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
270 void **ppStorage)
271{
272 NOREF(pvUser);
273 /* Validate input. */
274 AssertPtrReturn(ppStorage, VERR_INVALID_POINTER);
275 AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
276 AssertReturn((fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ, VERR_INVALID_PARAMETER);
277 RTFILE file;
278 int rc = RTFileFromNative(&file, RTFILE_NATIVE_STDIN);
279 if (RT_FAILURE(rc))
280 return rc;
281
282 /* No need to clear the buffer, the data will be read from disk. */
283 PFILEIOSTATE pFS = (PFILEIOSTATE)RTMemAlloc(sizeof(FILEIOSTATE));
284 if (!pFS)
285 return VERR_NO_MEMORY;
286
287 pFS->file = file;
288 pFS->off = 0;
289 pFS->offBuffer = UINT64_MAX;
290 pFS->cbBuffer = 0;
291
292 *ppStorage = pFS;
293 return VINF_SUCCESS;
294}
295
296static int convInClose(void *pvUser, void *pStorage)
297{
298 NOREF(pvUser);
299 AssertPtrReturn(pStorage, VERR_INVALID_POINTER);
300 PFILEIOSTATE pFS = (PFILEIOSTATE)pStorage;
301
302 RTMemFree(pFS);
303
304 return VINF_SUCCESS;
305}
306
307static int convInDelete(void *pvUser, const char *pcszFilename)
308{
309 NOREF(pvUser);
310 NOREF(pcszFilename);
311 AssertFailedReturn(VERR_NOT_SUPPORTED);
312}
313
314static int convInMove(void *pvUser, const char *pcszSrc, const char *pcszDst,
315 unsigned fMove)
316{
317 NOREF(pvUser);
318 NOREF(pcszSrc);
319 NOREF(pcszDst);
320 NOREF(fMove);
321 AssertFailedReturn(VERR_NOT_SUPPORTED);
322}
323
324static int convInGetFreeSpace(void *pvUser, const char *pcszFilename,
325 int64_t *pcbFreeSpace)
326{
327 NOREF(pvUser);
328 NOREF(pcszFilename);
329 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
330 *pcbFreeSpace = 0;
331 return VINF_SUCCESS;
332}
333
334static int convInGetModificationTime(void *pvUser, const char *pcszFilename,
335 PRTTIMESPEC pModificationTime)
336{
337 NOREF(pvUser);
338 NOREF(pcszFilename);
339 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
340 AssertFailedReturn(VERR_NOT_SUPPORTED);
341}
342
343static int convInGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
344{
345 NOREF(pvUser);
346 NOREF(pStorage);
347 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
348 AssertFailedReturn(VERR_NOT_SUPPORTED);
349}
350
351static int convInSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
352{
353 NOREF(pvUser);
354 NOREF(pStorage);
355 NOREF(cbSize);
356 AssertFailedReturn(VERR_NOT_SUPPORTED);
357}
358
359static int convInRead(void *pvUser, void *pStorage, uint64_t uOffset,
360 void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
361{
362 NOREF(pvUser);
363 AssertPtrReturn(pStorage, VERR_INVALID_POINTER);
364 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
365 PFILEIOSTATE pFS = (PFILEIOSTATE)pStorage;
366 AssertReturn(uOffset >= pFS->off, VERR_INVALID_PARAMETER);
367 int rc;
368
369 /* Fill buffer if it is empty. */
370 if (pFS->offBuffer == UINT64_MAX)
371 {
372 /* Repeat reading until buffer is full or EOF. */
373 size_t cbSumRead = 0, cbRead;
374 uint8_t *pTmp = (uint8_t *)&pFS->abBuffer[0];
375 size_t cbTmp = sizeof(pFS->abBuffer);
376 do
377 {
378 rc = RTFileRead(pFS->file, pTmp, cbTmp, &cbRead);
379 if (RT_FAILURE(rc))
380 return rc;
381 pTmp += cbRead;
382 cbTmp -= cbRead;
383 cbSumRead += cbRead;
384 } while (cbTmp && cbRead);
385
386 pFS->offBuffer = 0;
387 pFS->cbBuffer = cbSumRead;
388 }
389
390 /* Read several blocks and assemble the result if necessary */
391 size_t cbTotalRead = 0;
392 do
393 {
394 /* Skip over areas no one wants to read. */
395 while (uOffset > pFS->offBuffer + pFS->cbBuffer - 1)
396 {
397 if (pFS->cbBuffer < sizeof(pFS->abBuffer))
398 {
399 if (pcbRead)
400 *pcbRead = cbTotalRead;
401 return VERR_EOF;
402 }
403
404 /* Repeat reading until buffer is full or EOF. */
405 size_t cbSumRead = 0, cbRead;
406 uint8_t *pTmp = (uint8_t *)&pFS->abBuffer[0];
407 size_t cbTmp = sizeof(pFS->abBuffer);
408 do
409 {
410 rc = RTFileRead(pFS->file, pTmp, cbTmp, &cbRead);
411 if (RT_FAILURE(rc))
412 return rc;
413 pTmp += cbRead;
414 cbTmp -= cbRead;
415 cbSumRead += cbRead;
416 } while (cbTmp && cbRead);
417
418 pFS->offBuffer += pFS->cbBuffer;
419 pFS->cbBuffer = cbSumRead;
420 }
421
422 uint32_t cbThisRead = RT_MIN(cbBuffer,
423 pFS->cbBuffer - uOffset % sizeof(pFS->abBuffer));
424 memcpy(pvBuffer, &pFS->abBuffer[uOffset % sizeof(pFS->abBuffer)],
425 cbThisRead);
426 uOffset += cbThisRead;
427 pvBuffer = (uint8_t *)pvBuffer + cbThisRead;
428 cbBuffer -= cbThisRead;
429 cbTotalRead += cbThisRead;
430 } while (cbBuffer > 0);
431
432 if (pcbRead)
433 *pcbRead = cbTotalRead;
434
435 pFS->off = uOffset;
436
437 return VINF_SUCCESS;
438}
439
440static int convInWrite(void *pvUser, void *pStorage, uint64_t uOffset,
441 const void *pvBuffer, size_t cbBuffer,
442 size_t *pcbWritten)
443{
444 NOREF(pvUser);
445 NOREF(pStorage);
446 NOREF(uOffset);
447 NOREF(cbBuffer);
448 NOREF(pcbWritten);
449 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
450 AssertFailedReturn(VERR_NOT_SUPPORTED);
451}
452
453static int convInFlush(void *pvUser, void *pStorage)
454{
455 NOREF(pvUser);
456 NOREF(pStorage);
457 return VINF_SUCCESS;
458}
459
460static int convOutOpen(void *pvUser, const char *pszLocation,
461 uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
462 void **ppStorage)
463{
464 NOREF(pvUser);
465 /* Validate input. */
466 AssertPtrReturn(ppStorage, VERR_INVALID_POINTER);
467 AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
468 AssertReturn((fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_WRITE, VERR_INVALID_PARAMETER);
469 RTFILE file;
470 int rc = RTFileFromNative(&file, RTFILE_NATIVE_STDOUT);
471 if (RT_FAILURE(rc))
472 return rc;
473
474 /* Must clear buffer, so that skipped over data is initialized properly. */
475 PFILEIOSTATE pFS = (PFILEIOSTATE)RTMemAllocZ(sizeof(FILEIOSTATE));
476 if (!pFS)
477 return VERR_NO_MEMORY;
478
479 pFS->file = file;
480 pFS->off = 0;
481 pFS->offBuffer = 0;
482 pFS->cbBuffer = sizeof(FILEIOSTATE);
483
484 *ppStorage = pFS;
485 return VINF_SUCCESS;
486}
487
488static int convOutClose(void *pvUser, void *pStorage)
489{
490 NOREF(pvUser);
491 AssertPtrReturn(pStorage, VERR_INVALID_POINTER);
492 PFILEIOSTATE pFS = (PFILEIOSTATE)pStorage;
493 int rc = VINF_SUCCESS;
494
495 /* Flush any remaining buffer contents. */
496 if (pFS->cbBuffer)
497 rc = RTFileWrite(pFS->file, &pFS->abBuffer[0], pFS->cbBuffer, NULL);
498
499 RTMemFree(pFS);
500
501 return rc;
502}
503
504static int convOutDelete(void *pvUser, const char *pcszFilename)
505{
506 NOREF(pvUser);
507 NOREF(pcszFilename);
508 AssertFailedReturn(VERR_NOT_SUPPORTED);
509}
510
511static int convOutMove(void *pvUser, const char *pcszSrc, const char *pcszDst,
512 unsigned fMove)
513{
514 NOREF(pvUser);
515 NOREF(pcszSrc);
516 NOREF(pcszDst);
517 NOREF(fMove);
518 AssertFailedReturn(VERR_NOT_SUPPORTED);
519}
520
521static int convOutGetFreeSpace(void *pvUser, const char *pcszFilename,
522 int64_t *pcbFreeSpace)
523{
524 NOREF(pvUser);
525 NOREF(pcszFilename);
526 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
527 *pcbFreeSpace = INT64_MAX;
528 return VINF_SUCCESS;
529}
530
531static int convOutGetModificationTime(void *pvUser, const char *pcszFilename,
532 PRTTIMESPEC pModificationTime)
533{
534 NOREF(pvUser);
535 NOREF(pcszFilename);
536 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
537 AssertFailedReturn(VERR_NOT_SUPPORTED);
538}
539
540static int convOutGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
541{
542 NOREF(pvUser);
543 NOREF(pStorage);
544 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
545 AssertFailedReturn(VERR_NOT_SUPPORTED);
546}
547
548static int convOutSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
549{
550 NOREF(pvUser);
551 NOREF(pStorage);
552 NOREF(cbSize);
553 AssertFailedReturn(VERR_NOT_SUPPORTED);
554}
555
556static int convOutRead(void *pvUser, void *pStorage, uint64_t uOffset,
557 void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
558{
559 NOREF(pvUser);
560 NOREF(pStorage);
561 NOREF(uOffset);
562 NOREF(cbBuffer);
563 NOREF(pcbRead);
564 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
565 AssertFailedReturn(VERR_NOT_SUPPORTED);
566}
567
568static int convOutWrite(void *pvUser, void *pStorage, uint64_t uOffset,
569 const void *pvBuffer, size_t cbBuffer,
570 size_t *pcbWritten)
571{
572 NOREF(pvUser);
573 AssertPtrReturn(pStorage, VERR_INVALID_POINTER);
574 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
575 PFILEIOSTATE pFS = (PFILEIOSTATE)pStorage;
576 AssertReturn(uOffset >= pFS->off, VERR_INVALID_PARAMETER);
577 int rc;
578
579 /* Write the data to the buffer, flushing as required. */
580 size_t cbTotalWritten = 0;
581 do
582 {
583 /* Flush the buffer if we need a new one. */
584 while (uOffset > pFS->offBuffer + sizeof(pFS->abBuffer) - 1)
585 {
586 rc = RTFileWrite(pFS->file, &pFS->abBuffer[0],
587 sizeof(pFS->abBuffer), NULL);
588 RT_ZERO(pFS->abBuffer);
589 pFS->offBuffer += sizeof(pFS->abBuffer);
590 pFS->cbBuffer = 0;
591 }
592
593 uint32_t cbThisWrite = RT_MIN(cbBuffer,
594 sizeof(pFS->abBuffer) - uOffset % sizeof(pFS->abBuffer));
595 memcpy(&pFS->abBuffer[uOffset % sizeof(pFS->abBuffer)], pvBuffer,
596 cbThisWrite);
597 uOffset += cbThisWrite;
598 pvBuffer = (uint8_t *)pvBuffer + cbThisWrite;
599 cbBuffer -= cbThisWrite;
600 cbTotalWritten += cbThisWrite;
601 } while (cbBuffer > 0);
602
603 if (pcbWritten)
604 *pcbWritten = cbTotalWritten;
605
606 pFS->cbBuffer = uOffset % sizeof(pFS->abBuffer);
607 if (!pFS->cbBuffer)
608 pFS->cbBuffer = sizeof(pFS->abBuffer);
609 pFS->off = uOffset;
610
611 return VINF_SUCCESS;
612}
613
614static int convOutFlush(void *pvUser, void *pStorage)
615{
616 NOREF(pvUser);
617 NOREF(pStorage);
618 return VINF_SUCCESS;
619}
620
621int handleConvert(HandlerArg *a)
622{
623 const char *pszSrcFilename = NULL;
624 const char *pszDstFilename = NULL;
625 bool fStdIn = false;
626 bool fStdOut = false;
627 const char *pszSrcFormat = NULL;
628 VDTYPE enmSrcType = VDTYPE_HDD;
629 const char *pszDstFormat = NULL;
630 const char *pszVariant = NULL;
631 PVBOXHDD pSrcDisk = NULL;
632 PVBOXHDD pDstDisk = NULL;
633 unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
634 PVDINTERFACE pIfsImageInput = NULL;
635 PVDINTERFACE pIfsImageOutput = NULL;
636 VDINTERFACE IfsInputIO;
637 VDINTERFACE IfsOutputIO;
638 VDINTERFACEIO IfsInputIOCb;
639 VDINTERFACEIO IfsOutputIOCb;
640 int rc = VINF_SUCCESS;
641
642 /* Parse the command line. */
643 static const RTGETOPTDEF s_aOptions[] =
644 {
645 { "--srcfilename", 'i', RTGETOPT_REQ_STRING },
646 { "--dstfilename", 'o', RTGETOPT_REQ_STRING },
647 { "--stdin", 'p', RTGETOPT_REQ_NOTHING },
648 { "--stdout", 'P', RTGETOPT_REQ_NOTHING },
649 { "--srcformat", 's', RTGETOPT_REQ_STRING },
650 { "--dstformat", 'd', RTGETOPT_REQ_STRING },
651 { "--variant", 'v', RTGETOPT_REQ_STRING }
652 };
653 int ch;
654 RTGETOPTUNION ValueUnion;
655 RTGETOPTSTATE GetState;
656 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
657 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
658 {
659 switch (ch)
660 {
661 case 'i': // --srcfilename
662 pszSrcFilename = ValueUnion.psz;
663 break;
664 case 'o': // --dstfilename
665 pszDstFilename = ValueUnion.psz;
666 break;
667 case 'p': // --stdin
668 fStdIn = true;
669 break;
670 case 'P': // --stdout
671 fStdOut = true;
672 break;
673 case 's': // --srcformat
674 pszSrcFormat = ValueUnion.psz;
675 break;
676 case 'd': // --dstformat
677 pszDstFormat = ValueUnion.psz;
678 break;
679 case 'v': // --variant
680 pszVariant = ValueUnion.psz;
681 break;
682
683 default:
684 ch = RTGetOptPrintError(ch, &ValueUnion);
685 printUsage(g_pStdErr);
686 return ch;
687 }
688 }
689
690 /* Check for mandatory parameters and handle dummies/defaults. */
691 if (fStdIn && !pszSrcFormat)
692 return errorSyntax("Mandatory --srcformat option missing\n");
693 if (!pszDstFormat)
694 pszDstFormat = "VDI";
695 if (fStdIn && !pszSrcFilename)
696 {
697 /* Complete dummy, will be just passed to various calls to fulfill
698 * the "must be non-NULL" requirement, and is completely ignored
699 * otherwise. It shown in the stderr message below. */
700 pszSrcFilename = "stdin";
701 }
702 if (fStdOut && !pszDstFilename)
703 {
704 /* Will be stored in the destination image if it is a streamOptimized
705 * VMDK, but it isn't really relevant - use it for "branding". */
706 if (!RTStrICmp(pszDstFormat, "VMDK"))
707 pszDstFilename = "VirtualBoxStream.vmdk";
708 else
709 pszDstFilename = "stdout";
710 }
711 if (!pszSrcFilename)
712 return errorSyntax("Mandatory --srcfilename option missing\n");
713 if (!pszDstFilename)
714 return errorSyntax("Mandatory --dstfilename option missing\n");
715
716 if (fStdIn)
717 {
718 IfsInputIOCb.cbSize = sizeof(VDINTERFACEIO);
719 IfsInputIOCb.enmInterface = VDINTERFACETYPE_IO;
720 IfsInputIOCb.pfnOpen = convInOpen;
721 IfsInputIOCb.pfnClose = convInClose;
722 IfsInputIOCb.pfnDelete = convInDelete;
723 IfsInputIOCb.pfnMove = convInMove;
724 IfsInputIOCb.pfnGetFreeSpace = convInGetFreeSpace;
725 IfsInputIOCb.pfnGetModificationTime = convInGetModificationTime;
726 IfsInputIOCb.pfnGetSize = convInGetSize;
727 IfsInputIOCb.pfnSetSize = convInSetSize;
728 IfsInputIOCb.pfnReadSync = convInRead;
729 IfsInputIOCb.pfnWriteSync = convInWrite;
730 IfsInputIOCb.pfnFlushSync = convInFlush;
731 VDInterfaceAdd(&IfsInputIO, "stdin", VDINTERFACETYPE_IO,
732 &IfsInputIOCb, NULL, &pIfsImageInput);
733 }
734 if (fStdOut)
735 {
736 IfsOutputIOCb.cbSize = sizeof(VDINTERFACEIO);
737 IfsOutputIOCb.enmInterface = VDINTERFACETYPE_IO;
738 IfsOutputIOCb.pfnOpen = convOutOpen;
739 IfsOutputIOCb.pfnClose = convOutClose;
740 IfsOutputIOCb.pfnDelete = convOutDelete;
741 IfsOutputIOCb.pfnMove = convOutMove;
742 IfsOutputIOCb.pfnGetFreeSpace = convOutGetFreeSpace;
743 IfsOutputIOCb.pfnGetModificationTime = convOutGetModificationTime;
744 IfsOutputIOCb.pfnGetSize = convOutGetSize;
745 IfsOutputIOCb.pfnSetSize = convOutSetSize;
746 IfsOutputIOCb.pfnReadSync = convOutRead;
747 IfsOutputIOCb.pfnWriteSync = convOutWrite;
748 IfsOutputIOCb.pfnFlushSync = convOutFlush;
749 VDInterfaceAdd(&IfsOutputIO, "stdout", VDINTERFACETYPE_IO,
750 &IfsOutputIOCb, NULL, &pIfsImageOutput);
751 }
752
753 /* check the variant parameter */
754 if (pszVariant)
755 {
756 char *psz = (char*)pszVariant;
757 while (psz && *psz && RT_SUCCESS(rc))
758 {
759 size_t len;
760 const char *pszComma = strchr(psz, ',');
761 if (pszComma)
762 len = pszComma - psz;
763 else
764 len = strlen(psz);
765 if (len > 0)
766 {
767 if (!RTStrNICmp(pszVariant, "standard", len))
768 uImageFlags |= VD_IMAGE_FLAGS_NONE;
769 else if (!RTStrNICmp(pszVariant, "fixed", len))
770 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
771 else if (!RTStrNICmp(pszVariant, "split2g", len))
772 uImageFlags |= VD_VMDK_IMAGE_FLAGS_SPLIT_2G;
773 else if (!RTStrNICmp(pszVariant, "stream", len))
774 uImageFlags |= VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED;
775 else if (!RTStrNICmp(pszVariant, "esx", len))
776 uImageFlags |= VD_VMDK_IMAGE_FLAGS_ESX;
777 else
778 return errorSyntax("Invalid --variant option\n");
779 }
780 if (pszComma)
781 psz += len + 1;
782 else
783 psz += len;
784 }
785 }
786
787 do
788 {
789 /* try to determine input format if not specified */
790 if (!pszSrcFormat)
791 {
792 char *pszFormat = NULL;
793 VDTYPE enmType = VDTYPE_INVALID;
794 rc = VDGetFormat(NULL, NULL, pszSrcFilename, &pszFormat, &enmType);
795 if (RT_FAILURE(rc))
796 {
797 errorSyntax("No file format specified, please specify format: %Rrc\n", rc);
798 break;
799 }
800 pszSrcFormat = pszFormat;
801 enmSrcType = enmType;
802 }
803
804 rc = VDCreate(pVDIfs, enmSrcType, &pSrcDisk);
805 if (RT_FAILURE(rc))
806 {
807 errorRuntime("Error while creating source disk container: %Rrc\n", rc);
808 break;
809 }
810
811 rc = VDOpen(pSrcDisk, pszSrcFormat, pszSrcFilename,
812 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL,
813 pIfsImageInput);
814 if (RT_FAILURE(rc))
815 {
816 errorRuntime("Error while opening source image: %Rrc\n", rc);
817 break;
818 }
819
820 rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDstDisk);
821 if (RT_FAILURE(rc))
822 {
823 errorRuntime("Error while creating the destination disk container: %Rrc\n", rc);
824 break;
825 }
826
827 uint64_t cbSize = VDGetSize(pSrcDisk, VD_LAST_IMAGE);
828 RTStrmPrintf(g_pStdErr, "Converting image \"%s\" with size %RU64 bytes (%RU64MB)...\n", pszSrcFilename, cbSize, (cbSize + _1M - 1) / _1M);
829
830 /* Create the output image */
831 rc = VDCopy(pSrcDisk, VD_LAST_IMAGE, pDstDisk, pszDstFormat,
832 pszDstFilename, false, 0, uImageFlags, NULL,
833 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL, NULL,
834 pIfsImageOutput, NULL);
835 if (RT_FAILURE(rc))
836 {
837 errorRuntime("Error while copying the image: %Rrc\n", rc);
838 break;
839 }
840
841 }
842 while (0);
843
844 if (pDstDisk)
845 VDCloseAll(pDstDisk);
846 if (pSrcDisk)
847 VDCloseAll(pSrcDisk);
848
849 return RT_SUCCESS(rc) ? 0 : 1;
850}
851
852
853int handleInfo(HandlerArg *a)
854{
855 int rc = VINF_SUCCESS;
856 PVBOXHDD pDisk = NULL;
857 const char *pszFilename = NULL;
858
859 /* Parse the command line. */
860 static const RTGETOPTDEF s_aOptions[] =
861 {
862 { "--filename", 'f', RTGETOPT_REQ_STRING }
863 };
864 int ch;
865 RTGETOPTUNION ValueUnion;
866 RTGETOPTSTATE GetState;
867 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
868 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
869 {
870 switch (ch)
871 {
872 case 'f': // --filename
873 pszFilename = ValueUnion.psz;
874 break;
875
876 default:
877 ch = RTGetOptPrintError(ch, &ValueUnion);
878 printUsage(g_pStdErr);
879 return ch;
880 }
881 }
882
883 /* Check for mandatory parameters. */
884 if (!pszFilename)
885 return errorSyntax("Mandatory --filename option missing\n");
886
887 /* just try it */
888 char *pszFormat = NULL;
889 VDTYPE enmType = VDTYPE_INVALID;
890 rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat, &enmType);
891 if (RT_FAILURE(rc))
892 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
893
894 rc = VDCreate(pVDIfs, enmType, &pDisk);
895 if (RT_FAILURE(rc))
896 return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
897
898 /* Open the image */
899 rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO, NULL);
900 if (RT_FAILURE(rc))
901 return errorRuntime("Error while opening the image: %Rrc\n", rc);
902
903 VDDumpImages(pDisk);
904
905 VDCloseAll(pDisk);
906
907 return rc;
908}
909
910
911int handleCompact(HandlerArg *a)
912{
913 int rc = VINF_SUCCESS;
914 PVBOXHDD pDisk = NULL;
915 const char *pszFilename = NULL;
916
917 /* Parse the command line. */
918 static const RTGETOPTDEF s_aOptions[] =
919 {
920 { "--filename", 'f', RTGETOPT_REQ_STRING }
921 };
922 int ch;
923 RTGETOPTUNION ValueUnion;
924 RTGETOPTSTATE GetState;
925 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
926 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
927 {
928 switch (ch)
929 {
930 case 'f': // --filename
931 pszFilename = ValueUnion.psz;
932 break;
933
934 default:
935 ch = RTGetOptPrintError(ch, &ValueUnion);
936 printUsage(g_pStdErr);
937 return ch;
938 }
939 }
940
941 /* Check for mandatory parameters. */
942 if (!pszFilename)
943 return errorSyntax("Mandatory --filename option missing\n");
944
945 /* just try it */
946 char *pszFormat = NULL;
947 VDTYPE enmType = VDTYPE_INVALID;
948 rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat, &enmType);
949 if (RT_FAILURE(rc))
950 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
951
952 rc = VDCreate(pVDIfs, enmType, &pDisk);
953 if (RT_FAILURE(rc))
954 return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
955
956 /* Open the image */
957 rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
958 if (RT_FAILURE(rc))
959 return errorRuntime("Error while opening the image: %Rrc\n", rc);
960
961 rc = VDCompact(pDisk, 0, NULL);
962 if (RT_FAILURE(rc))
963 errorRuntime("Error while compacting image: %Rrc\n", rc);
964
965 VDCloseAll(pDisk);
966
967 return rc;
968}
969
970
971int main(int argc, char *argv[])
972{
973 RTR3Init();
974 int rc;
975 int exitcode = 0;
976
977 g_pszProgName = RTPathFilename(argv[0]);
978
979 bool fShowLogo = false;
980 int iCmd = 1;
981 int iCmdArg;
982
983 /* global options */
984 for (int i = 1; i < argc || argc <= iCmd; i++)
985 {
986 if ( argc <= iCmd
987 || !strcmp(argv[i], "help")
988 || !strcmp(argv[i], "-?")
989 || !strcmp(argv[i], "-h")
990 || !strcmp(argv[i], "-help")
991 || !strcmp(argv[i], "--help"))
992 {
993 showLogo(g_pStdOut);
994 printUsage(g_pStdOut);
995 return 0;
996 }
997
998 if ( !strcmp(argv[i], "-v")
999 || !strcmp(argv[i], "-version")
1000 || !strcmp(argv[i], "-Version")
1001 || !strcmp(argv[i], "--version"))
1002 {
1003 /* Print version number, and do nothing else. */
1004 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
1005 return 0;
1006 }
1007
1008 if ( !strcmp(argv[i], "--nologo")
1009 || !strcmp(argv[i], "-nologo")
1010 || !strcmp(argv[i], "-q"))
1011 {
1012 /* suppress the logo */
1013 fShowLogo = false;
1014 iCmd++;
1015 }
1016 else
1017 {
1018 break;
1019 }
1020 }
1021
1022 iCmdArg = iCmd + 1;
1023
1024 if (fShowLogo)
1025 showLogo(g_pStdOut);
1026
1027 /* initialize the VD backend with dummy handlers */
1028 VDINTERFACE vdInterfaceError;
1029 VDINTERFACEERROR vdInterfaceErrorCallbacks;
1030 vdInterfaceErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
1031 vdInterfaceErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
1032 vdInterfaceErrorCallbacks.pfnError = handleVDError;
1033 vdInterfaceErrorCallbacks.pfnMessage = handleVDMessage;
1034
1035 rc = VDInterfaceAdd(&vdInterfaceError, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1036 &vdInterfaceErrorCallbacks, NULL, &pVDIfs);
1037
1038 rc = VDInit();
1039 if (RT_FAILURE(rc))
1040 {
1041 errorSyntax("Initalizing backends failed! rc=%Rrc\n", rc);
1042 return 1;
1043 }
1044
1045 /*
1046 * All registered command handlers
1047 */
1048 static const struct
1049 {
1050 const char *command;
1051 int (*handler)(HandlerArg *a);
1052 } s_commandHandlers[] =
1053 {
1054 { "setuuid", handleSetUUID },
1055 { "convert", handleConvert },
1056 { "info", handleInfo },
1057 { "compact", handleCompact },
1058 { NULL, NULL }
1059 };
1060
1061 HandlerArg handlerArg = { 0, NULL };
1062 int commandIndex;
1063 for (commandIndex = 0; s_commandHandlers[commandIndex].command != NULL; commandIndex++)
1064 {
1065 if (!strcmp(s_commandHandlers[commandIndex].command, argv[iCmd]))
1066 {
1067 handlerArg.argc = argc - iCmdArg;
1068 handlerArg.argv = &argv[iCmdArg];
1069
1070 exitcode = s_commandHandlers[commandIndex].handler(&handlerArg);
1071 break;
1072 }
1073 }
1074 if (!s_commandHandlers[commandIndex].command)
1075 {
1076 errorSyntax("Invalid command '%s'", argv[iCmd]);
1077 return 1;
1078 }
1079
1080 rc = VDShutdown();
1081 if (RT_FAILURE(rc))
1082 {
1083 errorSyntax("Unloading backends failed! rc=%Rrc\n", rc);
1084 return 1;
1085 }
1086
1087 return exitcode;
1088}
1089
1090/* dummy stub for RuntimeR3 */
1091#ifndef RT_OS_WINDOWS
1092RTDECL(bool) RTAssertShouldPanic(void)
1093{
1094 return true;
1095}
1096#endif
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use