VirtualBox

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

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

VCI: Updates

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