VirtualBox

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

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

scm cleanup

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 18.5 KB
Line 
1/* $Id: vbox-img.cpp 32431 2010-09-11 18:02:17Z 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 showUsage(void)
36{
37 RTStrmPrintf(g_pStdErr,
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 " [--srcformat VDI|VMDK|VHD|RAW|..]\n"
48 " [--dstformat VDI|VMDK|VHD|RAW|..]\n"
49 " [--variant Standard,Fixed,Split2G,Stream,ESX]\n"
50 "\n"
51 " info --filename <filename>\n"
52 "\n"
53 " compact --filename <filename>\n",
54 g_pszProgName);
55}
56
57/** command handler argument */
58struct HandlerArg
59{
60 int argc;
61 char **argv;
62};
63
64PVDINTERFACE pVDIfs;
65
66static DECLCALLBACK(void) handleVDError(void *pvUser, int rc, RT_SRC_POS_DECL,
67 const char *pszFormat, va_list va)
68{
69 NOREF(pvUser);
70 NOREF(rc);
71 RTMsgErrorV(pszFormat, va);
72}
73
74static int handleVDMessage(void *pvUser, const char *pszFormat, ...)
75{
76 NOREF(pvUser);
77 va_list args;
78 va_start(args, pszFormat);
79 RTPrintfV(pszFormat, args);
80 va_end(args);
81 return VINF_SUCCESS;
82}
83
84/**
85 * Print a usage synopsis and the syntax error message.
86 */
87int errorSyntax(const char *pszFormat, ...)
88{
89 va_list args;
90
91 va_start(args, pszFormat);
92 RTPrintf("\n"
93 "Syntax error: %N\n", pszFormat, &args);
94 va_end(args);
95 showUsage();
96 return 1;
97}
98int errorRuntime(const char *pszFormat, ...)
99{
100 va_list args;
101
102 va_start(args, pszFormat);
103 RTPrintf("\n"
104 "Error: %N\n", pszFormat, &args);
105 va_end(args);
106 return 1;
107}
108
109
110
111int handleSetUUID(HandlerArg *a)
112{
113 const char *pszFilename = NULL;
114 char *pszFormat = NULL;
115 RTUUID imageUuid;
116 RTUUID parentUuid;
117 bool fSetImageUuid = false;
118 bool fSetParentUuid = false;
119 RTUuidClear(&imageUuid);
120 RTUuidClear(&parentUuid);
121 int rc;
122
123 /* Parse the command line. */
124 static const RTGETOPTDEF s_aOptions[] =
125 {
126 { "--filename", 'f', RTGETOPT_REQ_STRING },
127 { "--format", 'o', RTGETOPT_REQ_STRING },
128 { "--uuid", 'u', RTGETOPT_REQ_UUID },
129 { "--parentuuid", 'p', RTGETOPT_REQ_UUID },
130 { "--zeroparentuuid", 'P', RTGETOPT_REQ_NOTHING }
131 };
132 int ch;
133 RTGETOPTUNION ValueUnion;
134 RTGETOPTSTATE GetState;
135 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
136 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
137 {
138 switch (ch)
139 {
140 case 'f': // --filename
141 pszFilename = ValueUnion.psz;
142 break;
143 case 'o': // --format
144 pszFormat = RTStrDup(ValueUnion.psz);
145 break;
146 case 'u': // --uuid
147 imageUuid = ValueUnion.Uuid;
148 fSetImageUuid = true;
149 break;
150 case 'p': // --parentuuid
151 parentUuid = ValueUnion.Uuid;
152 fSetParentUuid = true;
153 break;
154 case 'P': // --zeroparentuuid
155 RTUuidClear(&parentUuid);
156 fSetParentUuid = true;
157 break;
158
159 default:
160 ch = RTGetOptPrintError(ch, &ValueUnion);
161 showUsage();
162 return ch;
163 }
164 }
165
166 /* Check for mandatory parameters. */
167 if (!pszFilename)
168 return errorSyntax("Mandatory --filename option missing\n");
169
170 /* Check for consistency of optional parameters. */
171 if (fSetImageUuid && RTUuidIsNull(&imageUuid))
172 return errorSyntax("Invalid parameter to --uuid option\n");
173
174 /* Autodetect image format. */
175 if (!pszFormat)
176 {
177 /* Don't pass error interface, as that would triggers error messages
178 * because some backends fail to open the image. */
179 rc = VDGetFormat(NULL, pszFilename, &pszFormat);
180 if (RT_FAILURE(rc))
181 return errorRuntime("Format autodetect failed: %Rrc\n", rc);
182 }
183
184 PVBOXHDD pVD = NULL;
185 rc = VDCreate(pVDIfs, &pVD);
186 if (RT_FAILURE(rc))
187 return errorRuntime("Cannot create the virtual disk container: %Rrc\n", rc);
188
189
190 rc = VDOpen(pVD, pszFormat, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
191 if (RT_FAILURE(rc))
192 return errorRuntime("Cannot open the virtual disk image \"%s\": %Rrc\n",
193 pszFilename, rc);
194
195 RTUUID oldImageUuid;
196 rc = VDGetUuid(pVD, VD_LAST_IMAGE, &oldImageUuid);
197 if (RT_FAILURE(rc))
198 return errorRuntime("Cannot get UUID of virtual disk image \"%s\": %Rrc\n",
199 pszFilename, rc);
200
201 RTPrintf("Old image UUID: %RTuuid\n", &oldImageUuid);
202
203 RTUUID oldParentUuid;
204 rc = VDGetParentUuid(pVD, VD_LAST_IMAGE, &oldParentUuid);
205 if (RT_FAILURE(rc))
206 return errorRuntime("Cannot get parent UUID of virtual disk image \"%s\": %Rrc\n",
207 pszFilename, rc);
208
209 RTPrintf("Old parent UUID: %RTuuid\n", &oldParentUuid);
210
211 if (fSetImageUuid)
212 {
213 RTPrintf("New image UUID: %RTuuid\n", &imageUuid);
214 rc = VDSetUuid(pVD, VD_LAST_IMAGE, &imageUuid);
215 if (RT_FAILURE(rc))
216 return errorRuntime("Cannot set UUID of virtual disk image \"%s\": %Rrc\n",
217 pszFilename, rc);
218 }
219
220 if (fSetParentUuid)
221 {
222 RTPrintf("New parent UUID: %RTuuid\n", &parentUuid);
223 rc = VDSetParentUuid(pVD, VD_LAST_IMAGE, &parentUuid);
224 if (RT_FAILURE(rc))
225 return errorRuntime("Cannot set parent UUID of virtual disk image \"%s\": %Rrc\n",
226 pszFilename, rc);
227 }
228
229 rc = VDCloseAll(pVD);
230 if (RT_FAILURE(rc))
231 return errorRuntime("Closing image failed! rc=%Rrc\n", rc);
232
233 if (pszFormat)
234 {
235 RTStrFree(pszFormat);
236 pszFormat = NULL;
237 }
238
239 return 0;
240}
241
242
243int handleConvert(HandlerArg *a)
244{
245 const char *pszSrcFilename = NULL;
246 const char *pszDstFilename = NULL;
247 char *pszSrcFormat = NULL;
248 char *pszDstFormat = NULL;
249 const char *pszVariant = NULL;
250 PVBOXHDD pSrcDisk = NULL;
251 PVBOXHDD pDstDisk = NULL;
252 unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
253 int rc = VINF_SUCCESS;
254
255 /* Parse the command line. */
256 static const RTGETOPTDEF s_aOptions[] =
257 {
258 { "--srcfilename", 'i', RTGETOPT_REQ_STRING },
259 { "--dstfilename", 'o', RTGETOPT_REQ_STRING },
260 { "--srcformat", 's', RTGETOPT_REQ_STRING },
261 { "--dstformat", 'd', RTGETOPT_REQ_STRING },
262 { "--variant", 'v', RTGETOPT_REQ_STRING }
263 };
264 int ch;
265 RTGETOPTUNION ValueUnion;
266 RTGETOPTSTATE GetState;
267 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
268 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
269 {
270 switch (ch)
271 {
272 case 'i': // --srcfilename
273 pszSrcFilename = ValueUnion.psz;
274 break;
275 case 'o': // --dstfilename
276 pszDstFilename = ValueUnion.psz;
277 break;
278 case 's': // --srcformat
279 pszSrcFormat = RTStrDup(ValueUnion.psz);
280 break;
281 case 'd': // --dstformat
282 pszDstFormat = RTStrDup(ValueUnion.psz);
283 break;
284 case 'v': // --variant
285 pszVariant = ValueUnion.psz;
286 break;
287
288 default:
289 ch = RTGetOptPrintError(ch, &ValueUnion);
290 showUsage();
291 return ch;
292 }
293 }
294
295 /* Check for mandatory parameters. */
296 if (!pszSrcFilename)
297 return errorSyntax("Mandatory --srcfilename option missing\n");
298 if (!pszDstFilename)
299 return errorSyntax("Mandatory --dstfilename option missing\n");
300
301 /* check the variant parameter */
302 if (pszVariant)
303 {
304 char *psz = (char*)pszVariant;
305 while (psz && *psz && RT_SUCCESS(rc))
306 {
307 size_t len;
308 const char *pszComma = strchr(psz, ',');
309 if (pszComma)
310 len = pszComma - psz;
311 else
312 len = strlen(psz);
313 if (len > 0)
314 {
315 if (!RTStrNICmp(pszVariant, "standard", len))
316 uImageFlags |= VD_IMAGE_FLAGS_NONE;
317 else if (!RTStrNICmp(pszVariant, "fixed", len))
318 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
319 else if (!RTStrNICmp(pszVariant, "split2g", len))
320 uImageFlags |= VD_VMDK_IMAGE_FLAGS_SPLIT_2G;
321 else if (!RTStrNICmp(pszVariant, "stream", len))
322 uImageFlags |= VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED;
323 else if (!RTStrNICmp(pszVariant, "esx", len))
324 uImageFlags |= VD_VMDK_IMAGE_FLAGS_ESX;
325 else
326 return errorSyntax("Invalid --variant option\n");
327 }
328 if (pszComma)
329 psz += len + 1;
330 else
331 psz += len;
332 }
333 }
334
335 do
336 {
337 /* try to determine input format if not specified */
338 if (!pszSrcFormat)
339 {
340 rc = VDGetFormat(NULL, pszSrcFilename, &pszSrcFormat);
341 if (RT_FAILURE(rc))
342 {
343 errorSyntax("No file format specified, please specify format: %Rrc\n", rc);
344 break;
345 }
346 }
347
348 rc = VDCreate(pVDIfs, &pSrcDisk);
349 if (RT_FAILURE(rc))
350 {
351 errorRuntime("Error while creating source disk container: %Rrc\n", rc);
352 break;
353 }
354
355 rc = VDOpen(pSrcDisk, pszSrcFormat, pszSrcFilename, VD_OPEN_FLAGS_READONLY, NULL);
356 if (RT_FAILURE(rc))
357 {
358 errorRuntime("Error while opening source image: %Rrc\n", rc);
359 break;
360 }
361
362 /* output format defaults to VDI */
363 if (!pszDstFormat)
364 pszDstFormat = "VDI";
365
366 rc = VDCreate(pVDIfs, &pDstDisk);
367 if (RT_FAILURE(rc))
368 {
369 errorRuntime("Error while creating the destination disk container: %Rrc\n", rc);
370 break;
371 }
372
373 uint64_t cbSize = VDGetSize(pSrcDisk, VD_LAST_IMAGE);
374 RTPrintf("Converting image \"%s\" with size %RU64 bytes (%RU64MB)...\n", pszSrcFilename, cbSize, (cbSize + _1M - 1) / _1M);
375
376 /* Create the output image */
377 rc = VDCopy(pSrcDisk, VD_LAST_IMAGE, pDstDisk, pszDstFormat,
378 pszDstFilename, false, 0, uImageFlags, NULL, NULL, NULL, NULL);
379 if (RT_FAILURE(rc))
380 {
381 errorRuntime("Error while copying the image: %Rrc\n", rc);
382 break;
383 }
384
385 }
386 while (0);
387
388 if (pDstDisk)
389 VDCloseAll(pDstDisk);
390 if (pSrcDisk)
391 VDCloseAll(pSrcDisk);
392
393 return RT_SUCCESS(rc) ? 0 : 1;
394}
395
396
397int handleInfo(HandlerArg *a)
398{
399 int rc = VINF_SUCCESS;
400 PVBOXHDD pDisk = NULL;
401 const char *pszFilename = NULL;
402
403 /* Parse the command line. */
404 static const RTGETOPTDEF s_aOptions[] =
405 {
406 { "--filename", 'f', RTGETOPT_REQ_STRING }
407 };
408 int ch;
409 RTGETOPTUNION ValueUnion;
410 RTGETOPTSTATE GetState;
411 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
412 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
413 {
414 switch (ch)
415 {
416 case 'f': // --filename
417 pszFilename = ValueUnion.psz;
418 break;
419
420 default:
421 ch = RTGetOptPrintError(ch, &ValueUnion);
422 showUsage();
423 return ch;
424 }
425 }
426
427 /* Check for mandatory parameters. */
428 if (!pszFilename)
429 return errorSyntax("Mandatory --filename option missing\n");
430
431 /* just try it */
432 char *pszFormat = NULL;
433 rc = VDGetFormat(NULL, pszFilename, &pszFormat);
434 if (RT_FAILURE(rc))
435 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
436
437 rc = VDCreate(pVDIfs, &pDisk);
438 if (RT_FAILURE(rc))
439 return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
440
441 /* Open the image */
442 rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO, NULL);
443 if (RT_FAILURE(rc))
444 return errorRuntime("Error while opening the image: %Rrc\n", rc);
445
446 VDDumpImages(pDisk);
447
448 VDCloseAll(pDisk);
449
450 return rc;
451}
452
453int handleCompact(HandlerArg *a)
454{
455 int rc = VINF_SUCCESS;
456 PVBOXHDD pDisk = NULL;
457 const char *pszFilename = NULL;
458
459 /* Parse the command line. */
460 static const RTGETOPTDEF s_aOptions[] =
461 {
462 { "--filename", 'f', RTGETOPT_REQ_STRING }
463 };
464 int ch;
465 RTGETOPTUNION ValueUnion;
466 RTGETOPTSTATE GetState;
467 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
468 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
469 {
470 switch (ch)
471 {
472 case 'f': // --filename
473 pszFilename = ValueUnion.psz;
474 break;
475
476 default:
477 ch = RTGetOptPrintError(ch, &ValueUnion);
478 showUsage();
479 return ch;
480 }
481 }
482
483 /* Check for mandatory parameters. */
484 if (!pszFilename)
485 return errorSyntax("Mandatory --filename option missing\n");
486
487 /* just try it */
488 char *pszFormat = NULL;
489 rc = VDGetFormat(NULL, pszFilename, &pszFormat);
490 if (RT_FAILURE(rc))
491 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
492
493 rc = VDCreate(pVDIfs, &pDisk);
494 if (RT_FAILURE(rc))
495 return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
496
497 /* Open the image */
498 rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
499 if (RT_FAILURE(rc))
500 return errorRuntime("Error while opening the image: %Rrc\n", rc);
501
502 rc = VDCompact(pDisk, 0, NULL);
503 if (RT_FAILURE(rc))
504 errorRuntime("Error while compacting image: %Rrc\n", rc);
505
506 VDCloseAll(pDisk);
507
508 return rc;
509}
510
511
512void showLogo()
513{
514 RTPrintf(VBOX_PRODUCT" Disk Utility "
515 VBOX_VERSION_STRING "\n"
516 "(C) 2005-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
517 "All rights reserved.\n"
518 "\n");
519}
520
521
522int main(int argc, char *argv[])
523{
524 RTR3Init();
525 int rc;
526
527 g_pszProgName = RTPathFilename(argv[0]);
528
529 bool fShowLogo = true;
530 int iCmd = 1;
531 int iCmdArg;
532
533 /* global options */
534 for (int i = 1; i < argc || argc <= iCmd; i++)
535 {
536 if ( argc <= iCmd
537 || !strcmp(argv[i], "help")
538 || !strcmp(argv[i], "-?")
539 || !strcmp(argv[i], "-h")
540 || !strcmp(argv[i], "-help")
541 || !strcmp(argv[i], "--help"))
542 {
543 showLogo();
544 showUsage();
545 return 0;
546 }
547
548 if ( !strcmp(argv[i], "-v")
549 || !strcmp(argv[i], "-version")
550 || !strcmp(argv[i], "-Version")
551 || !strcmp(argv[i], "--version"))
552 {
553 /* Print version number, and do nothing else. */
554 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
555 return 0;
556 }
557
558 if ( !strcmp(argv[i], "--nologo")
559 || !strcmp(argv[i], "-nologo")
560 || !strcmp(argv[i], "-q"))
561 {
562 /* suppress the logo */
563 fShowLogo = false;
564 iCmd++;
565 }
566 else
567 {
568 break;
569 }
570 }
571
572 iCmdArg = iCmd + 1;
573
574 if (fShowLogo)
575 showLogo();
576
577 /* initialize the VD backend with dummy handlers */
578 VDINTERFACE vdInterfaceError;
579 VDINTERFACEERROR vdInterfaceErrorCallbacks;
580 vdInterfaceErrorCallbacks.cbSize = sizeof(VDINTERFACEERROR);
581 vdInterfaceErrorCallbacks.enmInterface = VDINTERFACETYPE_ERROR;
582 vdInterfaceErrorCallbacks.pfnError = handleVDError;
583 vdInterfaceErrorCallbacks.pfnMessage = handleVDMessage;
584
585 rc = VDInterfaceAdd(&vdInterfaceError, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
586 &vdInterfaceErrorCallbacks, NULL, &pVDIfs);
587
588 rc = VDInit();
589 if (RT_FAILURE(rc))
590 return errorSyntax("Initalizing backends failed! rc=%Rrc\n");
591
592 /*
593 * All registered command handlers
594 */
595 static const struct
596 {
597 const char *command;
598 int (*handler)(HandlerArg *a);
599 } s_commandHandlers[] =
600 {
601 { "setuuid", handleSetUUID },
602 { "convert", handleConvert },
603 { "info", handleInfo },
604 { "compact", handleCompact },
605 { NULL, NULL }
606 };
607
608 HandlerArg handlerArg = { 0, NULL };
609 int commandIndex;
610 for (commandIndex = 0; s_commandHandlers[commandIndex].command != NULL; commandIndex++)
611 {
612 if (!strcmp(s_commandHandlers[commandIndex].command, argv[iCmd]))
613 {
614 handlerArg.argc = argc - iCmdArg;
615 handlerArg.argv = &argv[iCmdArg];
616
617 rc = s_commandHandlers[commandIndex].handler(&handlerArg);
618 break;
619 }
620 }
621 if (!s_commandHandlers[commandIndex].command)
622 return errorSyntax("Invalid command '%s'", argv[iCmd]);
623
624 rc = VDShutdown();
625 if (RT_FAILURE(rc))
626 return errorSyntax("Unloading backends failed! rc=%Rrc\n", rc);
627
628 return rc;
629}
630
631/* dummy stub for RuntimeR3 */
632#ifndef RT_OS_WINDOWS
633RTDECL(bool) RTAssertShouldPanic(void)
634{
635 return true;
636}
637#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