VirtualBox

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

Last change on this file since 45199 was 44528, checked in by vboxsync, 12 years ago

header (C) fixes

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 49.7 KB
Line 
1/* $Id: vbox-img.cpp 44528 2013-02-04 14:27:54Z vboxsync $ */
2/** @file
3 * Standalone image manipulation tool
4 */
5
6/*
7 * Copyright (C) 2010-2012 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#include <iprt/dvm.h>
34#include <iprt/filesystem.h>
35#include <iprt/vfs.h>
36
37const char *g_pszProgName = "";
38static void printUsage(PRTSTREAM pStrm)
39{
40 RTStrmPrintf(pStrm,
41 "Usage: %s\n"
42 " setuuid --filename <filename>\n"
43 " [--format VDI|VMDK|VHD|...]\n"
44 " [--uuid <uuid>]\n"
45 " [--parentuuid <uuid>]\n"
46 " [--zeroparentuuid]\n"
47 "\n"
48 " convert --srcfilename <filename>\n"
49 " --dstfilename <filename>\n"
50 " [--stdin]|[--stdout]\n"
51 " [--srcformat VDI|VMDK|VHD|RAW|..]\n"
52 " [--dstformat VDI|VMDK|VHD|RAW|..]\n"
53 " [--variant Standard,Fixed,Split2G,Stream,ESX]\n"
54 "\n"
55 " info --filename <filename>\n"
56 "\n"
57 " compact --filename <filename>\n"
58 " [--filesystemaware]\n"
59 "\n"
60 " createcache --filename <filename>\n"
61 " --size <cache size>\n"
62 "\n"
63 " createbase --filename <filename>\n"
64 " --size <size in bytes>\n"
65 " [--format VDI|VMDK|VHD] (default: VDI)\n"
66 " [--variant Standard,Fixed,Split2G,Stream,ESX]\n"
67 " [--dataalignment <alignment in bytes>]\n"
68 "\n"
69 " repair --filename <filename>\n"
70 " [--dry-run]\n"
71 " [--format VDI|VMDK|VHD] (default: autodetect)\n",
72 g_pszProgName);
73}
74
75void showLogo(PRTSTREAM pStrm)
76{
77 static bool s_fShown; /* show only once */
78
79 if (!s_fShown)
80 {
81 RTStrmPrintf(pStrm, VBOX_PRODUCT " Disk Utility " VBOX_VERSION_STRING "\n"
82 "(C) 2005-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
83 "All rights reserved.\n"
84 "\n");
85 s_fShown = true;
86 }
87}
88
89/** command handler argument */
90struct HandlerArg
91{
92 int argc;
93 char **argv;
94};
95
96PVDINTERFACE pVDIfs;
97
98static DECLCALLBACK(void) handleVDError(void *pvUser, int rc, RT_SRC_POS_DECL,
99 const char *pszFormat, va_list va)
100{
101 NOREF(pvUser);
102 NOREF(rc);
103 RTMsgErrorV(pszFormat, va);
104}
105
106static int handleVDMessage(void *pvUser, const char *pszFormat, va_list va)
107{
108 NOREF(pvUser);
109 RTPrintfV(pszFormat, va);
110 return VINF_SUCCESS;
111}
112
113/**
114 * Print a usage synopsis and the syntax error message.
115 */
116int errorSyntax(const char *pszFormat, ...)
117{
118 va_list args;
119 showLogo(g_pStdErr); // show logo even if suppressed
120 va_start(args, pszFormat);
121 RTStrmPrintf(g_pStdErr, "\nSyntax error: %N\n", pszFormat, &args);
122 va_end(args);
123 printUsage(g_pStdErr);
124 return 1;
125}
126
127int errorRuntime(const char *pszFormat, ...)
128{
129 va_list args;
130
131 va_start(args, pszFormat);
132 RTMsgErrorV(pszFormat, args);
133 va_end(args);
134 return 1;
135}
136
137static int parseDiskVariant(const char *psz, unsigned *puImageFlags)
138{
139 int rc = VINF_SUCCESS;
140 unsigned uImageFlags = *puImageFlags;
141
142 while (psz && *psz && RT_SUCCESS(rc))
143 {
144 size_t len;
145 const char *pszComma = strchr(psz, ',');
146 if (pszComma)
147 len = pszComma - psz;
148 else
149 len = strlen(psz);
150 if (len > 0)
151 {
152 /*
153 * Parsing is intentionally inconsistent: "standard" resets the
154 * variant, whereas the other flags are cumulative.
155 */
156 if (!RTStrNICmp(psz, "standard", len))
157 uImageFlags = VD_IMAGE_FLAGS_NONE;
158 else if ( !RTStrNICmp(psz, "fixed", len)
159 || !RTStrNICmp(psz, "static", len))
160 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
161 else if (!RTStrNICmp(psz, "Diff", len))
162 uImageFlags |= VD_IMAGE_FLAGS_DIFF;
163 else if (!RTStrNICmp(psz, "split2g", len))
164 uImageFlags |= VD_VMDK_IMAGE_FLAGS_SPLIT_2G;
165 else if ( !RTStrNICmp(psz, "stream", len)
166 || !RTStrNICmp(psz, "streamoptimized", len))
167 uImageFlags |= VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED;
168 else if (!RTStrNICmp(psz, "esx", len))
169 uImageFlags |= VD_VMDK_IMAGE_FLAGS_ESX;
170 else
171 rc = VERR_PARSE_ERROR;
172 }
173 if (pszComma)
174 psz += len + 1;
175 else
176 psz += len;
177 }
178
179 if (RT_SUCCESS(rc))
180 *puImageFlags = uImageFlags;
181 return rc;
182}
183
184
185int handleSetUUID(HandlerArg *a)
186{
187 const char *pszFilename = NULL;
188 char *pszFormat = NULL;
189 VDTYPE enmType = VDTYPE_INVALID;
190 RTUUID imageUuid;
191 RTUUID parentUuid;
192 bool fSetImageUuid = false;
193 bool fSetParentUuid = false;
194 RTUuidClear(&imageUuid);
195 RTUuidClear(&parentUuid);
196 int rc;
197
198 /* Parse the command line. */
199 static const RTGETOPTDEF s_aOptions[] =
200 {
201 { "--filename", 'f', RTGETOPT_REQ_STRING },
202 { "--format", 'o', RTGETOPT_REQ_STRING },
203 { "--uuid", 'u', RTGETOPT_REQ_UUID },
204 { "--parentuuid", 'p', RTGETOPT_REQ_UUID },
205 { "--zeroparentuuid", 'P', RTGETOPT_REQ_NOTHING }
206 };
207 int ch;
208 RTGETOPTUNION ValueUnion;
209 RTGETOPTSTATE GetState;
210 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
211 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
212 {
213 switch (ch)
214 {
215 case 'f': // --filename
216 pszFilename = ValueUnion.psz;
217 break;
218 case 'o': // --format
219 pszFormat = RTStrDup(ValueUnion.psz);
220 break;
221 case 'u': // --uuid
222 imageUuid = ValueUnion.Uuid;
223 fSetImageUuid = true;
224 break;
225 case 'p': // --parentuuid
226 parentUuid = ValueUnion.Uuid;
227 fSetParentUuid = true;
228 break;
229 case 'P': // --zeroparentuuid
230 RTUuidClear(&parentUuid);
231 fSetParentUuid = true;
232 break;
233
234 default:
235 ch = RTGetOptPrintError(ch, &ValueUnion);
236 printUsage(g_pStdErr);
237 return ch;
238 }
239 }
240
241 /* Check for mandatory parameters. */
242 if (!pszFilename)
243 return errorSyntax("Mandatory --filename option missing\n");
244
245 /* Check for consistency of optional parameters. */
246 if (fSetImageUuid && RTUuidIsNull(&imageUuid))
247 return errorSyntax("Invalid parameter to --uuid option\n");
248
249 /* Autodetect image format. */
250 if (!pszFormat)
251 {
252 /* Don't pass error interface, as that would triggers error messages
253 * because some backends fail to open the image. */
254 rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat, &enmType);
255 if (RT_FAILURE(rc))
256 return errorRuntime("Format autodetect failed: %Rrc\n", rc);
257 }
258
259 PVBOXHDD pVD = NULL;
260 rc = VDCreate(pVDIfs, enmType, &pVD);
261 if (RT_FAILURE(rc))
262 return errorRuntime("Cannot create the virtual disk container: %Rrc\n", rc);
263
264
265 rc = VDOpen(pVD, pszFormat, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
266 if (RT_FAILURE(rc))
267 return errorRuntime("Cannot open the virtual disk image \"%s\": %Rrc\n",
268 pszFilename, rc);
269
270 RTUUID oldImageUuid;
271 rc = VDGetUuid(pVD, VD_LAST_IMAGE, &oldImageUuid);
272 if (RT_FAILURE(rc))
273 return errorRuntime("Cannot get UUID of virtual disk image \"%s\": %Rrc\n",
274 pszFilename, rc);
275
276 RTPrintf("Old image UUID: %RTuuid\n", &oldImageUuid);
277
278 RTUUID oldParentUuid;
279 rc = VDGetParentUuid(pVD, VD_LAST_IMAGE, &oldParentUuid);
280 if (RT_FAILURE(rc))
281 return errorRuntime("Cannot get parent UUID of virtual disk image \"%s\": %Rrc\n",
282 pszFilename, rc);
283
284 RTPrintf("Old parent UUID: %RTuuid\n", &oldParentUuid);
285
286 if (fSetImageUuid)
287 {
288 RTPrintf("New image UUID: %RTuuid\n", &imageUuid);
289 rc = VDSetUuid(pVD, VD_LAST_IMAGE, &imageUuid);
290 if (RT_FAILURE(rc))
291 return errorRuntime("Cannot set UUID of virtual disk image \"%s\": %Rrc\n",
292 pszFilename, rc);
293 }
294
295 if (fSetParentUuid)
296 {
297 RTPrintf("New parent UUID: %RTuuid\n", &parentUuid);
298 rc = VDSetParentUuid(pVD, VD_LAST_IMAGE, &parentUuid);
299 if (RT_FAILURE(rc))
300 return errorRuntime("Cannot set parent UUID of virtual disk image \"%s\": %Rrc\n",
301 pszFilename, rc);
302 }
303
304 VDDestroy(pVD);
305
306 if (pszFormat)
307 {
308 RTStrFree(pszFormat);
309 pszFormat = NULL;
310 }
311
312 return 0;
313}
314
315
316typedef struct FILEIOSTATE
317{
318 RTFILE file;
319 /** Offset in the file. */
320 uint64_t off;
321 /** Offset where the buffer contents start. UINT64_MAX=buffer invalid. */
322 uint64_t offBuffer;
323 /** Size of valid data in the buffer. */
324 uint32_t cbBuffer;
325 /** Buffer for efficient I/O */
326 uint8_t abBuffer[16 *_1M];
327} FILEIOSTATE, *PFILEIOSTATE;
328
329static int convInOpen(void *pvUser, const char *pszLocation,
330 uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
331 void **ppStorage)
332{
333 NOREF(pvUser);
334 /* Validate input. */
335 AssertPtrReturn(ppStorage, VERR_INVALID_POINTER);
336 AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
337 AssertReturn((fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_READ, VERR_INVALID_PARAMETER);
338 RTFILE file;
339 int rc = RTFileFromNative(&file, RTFILE_NATIVE_STDIN);
340 if (RT_FAILURE(rc))
341 return rc;
342
343 /* No need to clear the buffer, the data will be read from disk. */
344 PFILEIOSTATE pFS = (PFILEIOSTATE)RTMemAlloc(sizeof(FILEIOSTATE));
345 if (!pFS)
346 return VERR_NO_MEMORY;
347
348 pFS->file = file;
349 pFS->off = 0;
350 pFS->offBuffer = UINT64_MAX;
351 pFS->cbBuffer = 0;
352
353 *ppStorage = pFS;
354 return VINF_SUCCESS;
355}
356
357static int convInClose(void *pvUser, void *pStorage)
358{
359 NOREF(pvUser);
360 AssertPtrReturn(pStorage, VERR_INVALID_POINTER);
361 PFILEIOSTATE pFS = (PFILEIOSTATE)pStorage;
362
363 RTMemFree(pFS);
364
365 return VINF_SUCCESS;
366}
367
368static int convInDelete(void *pvUser, const char *pcszFilename)
369{
370 NOREF(pvUser);
371 NOREF(pcszFilename);
372 AssertFailedReturn(VERR_NOT_SUPPORTED);
373}
374
375static int convInMove(void *pvUser, const char *pcszSrc, const char *pcszDst,
376 unsigned fMove)
377{
378 NOREF(pvUser);
379 NOREF(pcszSrc);
380 NOREF(pcszDst);
381 NOREF(fMove);
382 AssertFailedReturn(VERR_NOT_SUPPORTED);
383}
384
385static int convInGetFreeSpace(void *pvUser, const char *pcszFilename,
386 int64_t *pcbFreeSpace)
387{
388 NOREF(pvUser);
389 NOREF(pcszFilename);
390 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
391 *pcbFreeSpace = 0;
392 return VINF_SUCCESS;
393}
394
395static int convInGetModificationTime(void *pvUser, const char *pcszFilename,
396 PRTTIMESPEC pModificationTime)
397{
398 NOREF(pvUser);
399 NOREF(pcszFilename);
400 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
401 AssertFailedReturn(VERR_NOT_SUPPORTED);
402}
403
404static int convInGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
405{
406 NOREF(pvUser);
407 NOREF(pStorage);
408 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
409 AssertFailedReturn(VERR_NOT_SUPPORTED);
410}
411
412static int convInSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
413{
414 NOREF(pvUser);
415 NOREF(pStorage);
416 NOREF(cbSize);
417 AssertFailedReturn(VERR_NOT_SUPPORTED);
418}
419
420static int convInRead(void *pvUser, void *pStorage, uint64_t uOffset,
421 void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
422{
423 NOREF(pvUser);
424 AssertPtrReturn(pStorage, VERR_INVALID_POINTER);
425 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
426 PFILEIOSTATE pFS = (PFILEIOSTATE)pStorage;
427 AssertReturn(uOffset >= pFS->off, VERR_INVALID_PARAMETER);
428 int rc;
429
430 /* Fill buffer if it is empty. */
431 if (pFS->offBuffer == UINT64_MAX)
432 {
433 /* Repeat reading until buffer is full or EOF. */
434 size_t cbSumRead = 0, cbRead;
435 uint8_t *pTmp = (uint8_t *)&pFS->abBuffer[0];
436 size_t cbTmp = sizeof(pFS->abBuffer);
437 do
438 {
439 rc = RTFileRead(pFS->file, pTmp, cbTmp, &cbRead);
440 if (RT_FAILURE(rc))
441 return rc;
442 pTmp += cbRead;
443 cbTmp -= cbRead;
444 cbSumRead += cbRead;
445 } while (cbTmp && cbRead);
446
447 pFS->offBuffer = 0;
448 pFS->cbBuffer = cbSumRead;
449 if (!cbSumRead && !pcbRead) /* Caller can't handle partial reads. */
450 return VERR_EOF;
451 }
452
453 /* Read several blocks and assemble the result if necessary */
454 size_t cbTotalRead = 0;
455 do
456 {
457 /* Skip over areas no one wants to read. */
458 while (uOffset > pFS->offBuffer + pFS->cbBuffer - 1)
459 {
460 if (pFS->cbBuffer < sizeof(pFS->abBuffer))
461 {
462 if (pcbRead)
463 *pcbRead = cbTotalRead;
464 return VERR_EOF;
465 }
466
467 /* Repeat reading until buffer is full or EOF. */
468 size_t cbSumRead = 0, cbRead;
469 uint8_t *pTmp = (uint8_t *)&pFS->abBuffer[0];
470 size_t cbTmp = sizeof(pFS->abBuffer);
471 do
472 {
473 rc = RTFileRead(pFS->file, pTmp, cbTmp, &cbRead);
474 if (RT_FAILURE(rc))
475 return rc;
476 pTmp += cbRead;
477 cbTmp -= cbRead;
478 cbSumRead += cbRead;
479 } while (cbTmp && cbRead);
480
481 pFS->offBuffer += pFS->cbBuffer;
482 pFS->cbBuffer = cbSumRead;
483 }
484
485 uint32_t cbThisRead = RT_MIN(cbBuffer,
486 pFS->cbBuffer - uOffset % sizeof(pFS->abBuffer));
487 memcpy(pvBuffer, &pFS->abBuffer[uOffset % sizeof(pFS->abBuffer)],
488 cbThisRead);
489 uOffset += cbThisRead;
490 pvBuffer = (uint8_t *)pvBuffer + cbThisRead;
491 cbBuffer -= cbThisRead;
492 cbTotalRead += cbThisRead;
493 if (!cbTotalRead && !pcbRead) /* Caller can't handle partial reads. */
494 return VERR_EOF;
495 } while (cbBuffer > 0);
496
497 if (pcbRead)
498 *pcbRead = cbTotalRead;
499
500 pFS->off = uOffset;
501
502 return VINF_SUCCESS;
503}
504
505static int convInWrite(void *pvUser, void *pStorage, uint64_t uOffset,
506 const void *pvBuffer, size_t cbBuffer,
507 size_t *pcbWritten)
508{
509 NOREF(pvUser);
510 NOREF(pStorage);
511 NOREF(uOffset);
512 NOREF(cbBuffer);
513 NOREF(pcbWritten);
514 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
515 AssertFailedReturn(VERR_NOT_SUPPORTED);
516}
517
518static int convInFlush(void *pvUser, void *pStorage)
519{
520 NOREF(pvUser);
521 NOREF(pStorage);
522 return VINF_SUCCESS;
523}
524
525static int convOutOpen(void *pvUser, const char *pszLocation,
526 uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
527 void **ppStorage)
528{
529 NOREF(pvUser);
530 /* Validate input. */
531 AssertPtrReturn(ppStorage, VERR_INVALID_POINTER);
532 AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
533 AssertReturn((fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_WRITE, VERR_INVALID_PARAMETER);
534 RTFILE file;
535 int rc = RTFileFromNative(&file, RTFILE_NATIVE_STDOUT);
536 if (RT_FAILURE(rc))
537 return rc;
538
539 /* Must clear buffer, so that skipped over data is initialized properly. */
540 PFILEIOSTATE pFS = (PFILEIOSTATE)RTMemAllocZ(sizeof(FILEIOSTATE));
541 if (!pFS)
542 return VERR_NO_MEMORY;
543
544 pFS->file = file;
545 pFS->off = 0;
546 pFS->offBuffer = 0;
547 pFS->cbBuffer = sizeof(FILEIOSTATE);
548
549 *ppStorage = pFS;
550 return VINF_SUCCESS;
551}
552
553static int convOutClose(void *pvUser, void *pStorage)
554{
555 NOREF(pvUser);
556 AssertPtrReturn(pStorage, VERR_INVALID_POINTER);
557 PFILEIOSTATE pFS = (PFILEIOSTATE)pStorage;
558 int rc = VINF_SUCCESS;
559
560 /* Flush any remaining buffer contents. */
561 if (pFS->cbBuffer)
562 rc = RTFileWrite(pFS->file, &pFS->abBuffer[0], pFS->cbBuffer, NULL);
563
564 RTMemFree(pFS);
565
566 return rc;
567}
568
569static int convOutDelete(void *pvUser, const char *pcszFilename)
570{
571 NOREF(pvUser);
572 NOREF(pcszFilename);
573 AssertFailedReturn(VERR_NOT_SUPPORTED);
574}
575
576static int convOutMove(void *pvUser, const char *pcszSrc, const char *pcszDst,
577 unsigned fMove)
578{
579 NOREF(pvUser);
580 NOREF(pcszSrc);
581 NOREF(pcszDst);
582 NOREF(fMove);
583 AssertFailedReturn(VERR_NOT_SUPPORTED);
584}
585
586static int convOutGetFreeSpace(void *pvUser, const char *pcszFilename,
587 int64_t *pcbFreeSpace)
588{
589 NOREF(pvUser);
590 NOREF(pcszFilename);
591 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
592 *pcbFreeSpace = INT64_MAX;
593 return VINF_SUCCESS;
594}
595
596static int convOutGetModificationTime(void *pvUser, const char *pcszFilename,
597 PRTTIMESPEC pModificationTime)
598{
599 NOREF(pvUser);
600 NOREF(pcszFilename);
601 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
602 AssertFailedReturn(VERR_NOT_SUPPORTED);
603}
604
605static int convOutGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
606{
607 NOREF(pvUser);
608 NOREF(pStorage);
609 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
610 AssertFailedReturn(VERR_NOT_SUPPORTED);
611}
612
613static int convOutSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
614{
615 NOREF(pvUser);
616 NOREF(pStorage);
617 NOREF(cbSize);
618 AssertFailedReturn(VERR_NOT_SUPPORTED);
619}
620
621static int convOutRead(void *pvUser, void *pStorage, uint64_t uOffset,
622 void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
623{
624 NOREF(pvUser);
625 NOREF(pStorage);
626 NOREF(uOffset);
627 NOREF(cbBuffer);
628 NOREF(pcbRead);
629 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
630 AssertFailedReturn(VERR_NOT_SUPPORTED);
631}
632
633static int convOutWrite(void *pvUser, void *pStorage, uint64_t uOffset,
634 const void *pvBuffer, size_t cbBuffer,
635 size_t *pcbWritten)
636{
637 NOREF(pvUser);
638 AssertPtrReturn(pStorage, VERR_INVALID_POINTER);
639 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
640 PFILEIOSTATE pFS = (PFILEIOSTATE)pStorage;
641 AssertReturn(uOffset >= pFS->off, VERR_INVALID_PARAMETER);
642 int rc;
643
644 /* Write the data to the buffer, flushing as required. */
645 size_t cbTotalWritten = 0;
646 do
647 {
648 /* Flush the buffer if we need a new one. */
649 while (uOffset > pFS->offBuffer + sizeof(pFS->abBuffer) - 1)
650 {
651 rc = RTFileWrite(pFS->file, &pFS->abBuffer[0],
652 sizeof(pFS->abBuffer), NULL);
653 RT_ZERO(pFS->abBuffer);
654 pFS->offBuffer += sizeof(pFS->abBuffer);
655 pFS->cbBuffer = 0;
656 }
657
658 uint32_t cbThisWrite = RT_MIN(cbBuffer,
659 sizeof(pFS->abBuffer) - uOffset % sizeof(pFS->abBuffer));
660 memcpy(&pFS->abBuffer[uOffset % sizeof(pFS->abBuffer)], pvBuffer,
661 cbThisWrite);
662 uOffset += cbThisWrite;
663 pvBuffer = (uint8_t *)pvBuffer + cbThisWrite;
664 cbBuffer -= cbThisWrite;
665 cbTotalWritten += cbThisWrite;
666 } while (cbBuffer > 0);
667
668 if (pcbWritten)
669 *pcbWritten = cbTotalWritten;
670
671 pFS->cbBuffer = uOffset % sizeof(pFS->abBuffer);
672 if (!pFS->cbBuffer)
673 pFS->cbBuffer = sizeof(pFS->abBuffer);
674 pFS->off = uOffset;
675
676 return VINF_SUCCESS;
677}
678
679static int convOutFlush(void *pvUser, void *pStorage)
680{
681 NOREF(pvUser);
682 NOREF(pStorage);
683 return VINF_SUCCESS;
684}
685
686int handleConvert(HandlerArg *a)
687{
688 const char *pszSrcFilename = NULL;
689 const char *pszDstFilename = NULL;
690 bool fStdIn = false;
691 bool fStdOut = false;
692 const char *pszSrcFormat = NULL;
693 VDTYPE enmSrcType = VDTYPE_HDD;
694 const char *pszDstFormat = NULL;
695 const char *pszVariant = NULL;
696 PVBOXHDD pSrcDisk = NULL;
697 PVBOXHDD pDstDisk = NULL;
698 unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
699 PVDINTERFACE pIfsImageInput = NULL;
700 PVDINTERFACE pIfsImageOutput = NULL;
701 VDINTERFACEIO IfsInputIO;
702 VDINTERFACEIO IfsOutputIO;
703 int rc = VINF_SUCCESS;
704
705 /* Parse the command line. */
706 static const RTGETOPTDEF s_aOptions[] =
707 {
708 { "--srcfilename", 'i', RTGETOPT_REQ_STRING },
709 { "--dstfilename", 'o', RTGETOPT_REQ_STRING },
710 { "--stdin", 'p', RTGETOPT_REQ_NOTHING },
711 { "--stdout", 'P', RTGETOPT_REQ_NOTHING },
712 { "--srcformat", 's', RTGETOPT_REQ_STRING },
713 { "--dstformat", 'd', RTGETOPT_REQ_STRING },
714 { "--variant", 'v', RTGETOPT_REQ_STRING }
715 };
716 int ch;
717 RTGETOPTUNION ValueUnion;
718 RTGETOPTSTATE GetState;
719 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
720 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
721 {
722 switch (ch)
723 {
724 case 'i': // --srcfilename
725 pszSrcFilename = ValueUnion.psz;
726 break;
727 case 'o': // --dstfilename
728 pszDstFilename = ValueUnion.psz;
729 break;
730 case 'p': // --stdin
731 fStdIn = true;
732 break;
733 case 'P': // --stdout
734 fStdOut = true;
735 break;
736 case 's': // --srcformat
737 pszSrcFormat = ValueUnion.psz;
738 break;
739 case 'd': // --dstformat
740 pszDstFormat = ValueUnion.psz;
741 break;
742 case 'v': // --variant
743 pszVariant = ValueUnion.psz;
744 break;
745
746 default:
747 ch = RTGetOptPrintError(ch, &ValueUnion);
748 printUsage(g_pStdErr);
749 return ch;
750 }
751 }
752
753 /* Check for mandatory parameters and handle dummies/defaults. */
754 if (fStdIn && !pszSrcFormat)
755 return errorSyntax("Mandatory --srcformat option missing\n");
756 if (!pszDstFormat)
757 pszDstFormat = "VDI";
758 if (fStdIn && !pszSrcFilename)
759 {
760 /* Complete dummy, will be just passed to various calls to fulfill
761 * the "must be non-NULL" requirement, and is completely ignored
762 * otherwise. It shown in the stderr message below. */
763 pszSrcFilename = "stdin";
764 }
765 if (fStdOut && !pszDstFilename)
766 {
767 /* Will be stored in the destination image if it is a streamOptimized
768 * VMDK, but it isn't really relevant - use it for "branding". */
769 if (!RTStrICmp(pszDstFormat, "VMDK"))
770 pszDstFilename = "VirtualBoxStream.vmdk";
771 else
772 pszDstFilename = "stdout";
773 }
774 if (!pszSrcFilename)
775 return errorSyntax("Mandatory --srcfilename option missing\n");
776 if (!pszDstFilename)
777 return errorSyntax("Mandatory --dstfilename option missing\n");
778
779 if (fStdIn)
780 {
781 IfsInputIO.pfnOpen = convInOpen;
782 IfsInputIO.pfnClose = convInClose;
783 IfsInputIO.pfnDelete = convInDelete;
784 IfsInputIO.pfnMove = convInMove;
785 IfsInputIO.pfnGetFreeSpace = convInGetFreeSpace;
786 IfsInputIO.pfnGetModificationTime = convInGetModificationTime;
787 IfsInputIO.pfnGetSize = convInGetSize;
788 IfsInputIO.pfnSetSize = convInSetSize;
789 IfsInputIO.pfnReadSync = convInRead;
790 IfsInputIO.pfnWriteSync = convInWrite;
791 IfsInputIO.pfnFlushSync = convInFlush;
792 VDInterfaceAdd(&IfsInputIO.Core, "stdin", VDINTERFACETYPE_IO,
793 NULL, sizeof(VDINTERFACEIO), &pIfsImageInput);
794 }
795 if (fStdOut)
796 {
797 IfsOutputIO.pfnOpen = convOutOpen;
798 IfsOutputIO.pfnClose = convOutClose;
799 IfsOutputIO.pfnDelete = convOutDelete;
800 IfsOutputIO.pfnMove = convOutMove;
801 IfsOutputIO.pfnGetFreeSpace = convOutGetFreeSpace;
802 IfsOutputIO.pfnGetModificationTime = convOutGetModificationTime;
803 IfsOutputIO.pfnGetSize = convOutGetSize;
804 IfsOutputIO.pfnSetSize = convOutSetSize;
805 IfsOutputIO.pfnReadSync = convOutRead;
806 IfsOutputIO.pfnWriteSync = convOutWrite;
807 IfsOutputIO.pfnFlushSync = convOutFlush;
808 VDInterfaceAdd(&IfsOutputIO.Core, "stdout", VDINTERFACETYPE_IO,
809 NULL, sizeof(VDINTERFACEIO), &pIfsImageOutput);
810 }
811
812 /* check the variant parameter */
813 if (pszVariant)
814 {
815 char *psz = (char*)pszVariant;
816 while (psz && *psz && RT_SUCCESS(rc))
817 {
818 size_t len;
819 const char *pszComma = strchr(psz, ',');
820 if (pszComma)
821 len = pszComma - psz;
822 else
823 len = strlen(psz);
824 if (len > 0)
825 {
826 if (!RTStrNICmp(pszVariant, "standard", len))
827 uImageFlags |= VD_IMAGE_FLAGS_NONE;
828 else if (!RTStrNICmp(pszVariant, "fixed", len))
829 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
830 else if (!RTStrNICmp(pszVariant, "split2g", len))
831 uImageFlags |= VD_VMDK_IMAGE_FLAGS_SPLIT_2G;
832 else if (!RTStrNICmp(pszVariant, "stream", len))
833 uImageFlags |= VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED;
834 else if (!RTStrNICmp(pszVariant, "esx", len))
835 uImageFlags |= VD_VMDK_IMAGE_FLAGS_ESX;
836 else
837 return errorSyntax("Invalid --variant option\n");
838 }
839 if (pszComma)
840 psz += len + 1;
841 else
842 psz += len;
843 }
844 }
845
846 do
847 {
848 /* try to determine input format if not specified */
849 if (!pszSrcFormat)
850 {
851 char *pszFormat = NULL;
852 VDTYPE enmType = VDTYPE_INVALID;
853 rc = VDGetFormat(NULL, NULL, pszSrcFilename, &pszFormat, &enmType);
854 if (RT_FAILURE(rc))
855 {
856 errorSyntax("No file format specified, please specify format: %Rrc\n", rc);
857 break;
858 }
859 pszSrcFormat = pszFormat;
860 enmSrcType = enmType;
861 }
862
863 rc = VDCreate(pVDIfs, enmSrcType, &pSrcDisk);
864 if (RT_FAILURE(rc))
865 {
866 errorRuntime("Error while creating source disk container: %Rrc\n", rc);
867 break;
868 }
869
870 rc = VDOpen(pSrcDisk, pszSrcFormat, pszSrcFilename,
871 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL,
872 pIfsImageInput);
873 if (RT_FAILURE(rc))
874 {
875 errorRuntime("Error while opening source image: %Rrc\n", rc);
876 break;
877 }
878
879 rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDstDisk);
880 if (RT_FAILURE(rc))
881 {
882 errorRuntime("Error while creating the destination disk container: %Rrc\n", rc);
883 break;
884 }
885
886 uint64_t cbSize = VDGetSize(pSrcDisk, VD_LAST_IMAGE);
887 RTStrmPrintf(g_pStdErr, "Converting image \"%s\" with size %RU64 bytes (%RU64MB)...\n", pszSrcFilename, cbSize, (cbSize + _1M - 1) / _1M);
888
889 /* Create the output image */
890 rc = VDCopy(pSrcDisk, VD_LAST_IMAGE, pDstDisk, pszDstFormat,
891 pszDstFilename, false, 0, uImageFlags, NULL,
892 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL, NULL,
893 pIfsImageOutput, NULL);
894 if (RT_FAILURE(rc))
895 {
896 errorRuntime("Error while copying the image: %Rrc\n", rc);
897 break;
898 }
899
900 }
901 while (0);
902
903 if (pDstDisk)
904 VDDestroy(pDstDisk);
905 if (pSrcDisk)
906 VDDestroy(pSrcDisk);
907
908 return RT_SUCCESS(rc) ? 0 : 1;
909}
910
911
912int handleInfo(HandlerArg *a)
913{
914 int rc = VINF_SUCCESS;
915 PVBOXHDD pDisk = NULL;
916 const char *pszFilename = NULL;
917
918 /* Parse the command line. */
919 static const RTGETOPTDEF s_aOptions[] =
920 {
921 { "--filename", 'f', RTGETOPT_REQ_STRING }
922 };
923 int ch;
924 RTGETOPTUNION ValueUnion;
925 RTGETOPTSTATE GetState;
926 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
927 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
928 {
929 switch (ch)
930 {
931 case 'f': // --filename
932 pszFilename = ValueUnion.psz;
933 break;
934
935 default:
936 ch = RTGetOptPrintError(ch, &ValueUnion);
937 printUsage(g_pStdErr);
938 return ch;
939 }
940 }
941
942 /* Check for mandatory parameters. */
943 if (!pszFilename)
944 return errorSyntax("Mandatory --filename option missing\n");
945
946 /* just try it */
947 char *pszFormat = NULL;
948 VDTYPE enmType = VDTYPE_INVALID;
949 rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat, &enmType);
950 if (RT_FAILURE(rc))
951 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
952
953 rc = VDCreate(pVDIfs, enmType, &pDisk);
954 if (RT_FAILURE(rc))
955 return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
956
957 /* Open the image */
958 rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO, NULL);
959 if (RT_FAILURE(rc))
960 return errorRuntime("Error while opening the image: %Rrc\n", rc);
961
962 VDDumpImages(pDisk);
963
964 VDDestroy(pDisk);
965
966 return rc;
967}
968
969
970static DECLCALLBACK(int) vboximgDvmRead(void *pvUser, uint64_t off, void *pvBuf, size_t cbRead)
971{
972 int rc = VINF_SUCCESS;
973 PVBOXHDD pDisk = (PVBOXHDD)pvUser;
974
975 /* Take shortcut if possible. */
976 if ( off % 512 == 0
977 && cbRead % 512 == 0)
978 rc = VDRead(pDisk, off, pvBuf, cbRead);
979 else
980 {
981 uint8_t *pbBuf = (uint8_t *)pvBuf;
982 uint8_t abBuf[512];
983
984 /* Unaligned access, make it aligned. */
985 if (off % 512 != 0)
986 {
987 uint64_t offAligned = off & ~(uint64_t)(512 - 1);
988 size_t cbToCopy = 512 - (off - offAligned);
989 rc = VDRead(pDisk, offAligned, abBuf, 512);
990 if (RT_SUCCESS(rc))
991 {
992 memcpy(pbBuf, &abBuf[off - offAligned], cbToCopy);
993 pbBuf += cbToCopy;
994 off += cbToCopy;
995 cbRead -= cbToCopy;
996 }
997 }
998
999 if ( RT_SUCCESS(rc)
1000 && (cbRead & ~(uint64_t)(512 - 1)))
1001 {
1002 size_t cbReadAligned = cbRead & ~(uint64_t)(512 - 1);
1003
1004 Assert(!(off % 512));
1005 rc = VDRead(pDisk, off, pbBuf, cbReadAligned);
1006 if (RT_SUCCESS(rc))
1007 {
1008 pbBuf += cbReadAligned;
1009 off += cbReadAligned;
1010 cbRead -= cbReadAligned;
1011 }
1012 }
1013
1014 if ( RT_SUCCESS(rc)
1015 && cbRead)
1016 {
1017 Assert(cbRead < 512);
1018 Assert(!(off % 512));
1019
1020 rc = VDRead(pDisk, off, abBuf, 512);
1021 if (RT_SUCCESS(rc))
1022 memcpy(pbBuf, abBuf, cbRead);
1023 }
1024 }
1025
1026 return rc;
1027}
1028
1029
1030static DECLCALLBACK(int) vboximgDvmWrite(void *pvUser, uint64_t off, const void *pvBuf, size_t cbWrite)
1031{
1032 PVBOXHDD pDisk = (PVBOXHDD)pvUser;
1033 return VDWrite(pDisk, off, pvBuf, cbWrite);
1034}
1035
1036
1037static DECLCALLBACK(int) vboximgQueryBlockStatus(void *pvUser, uint64_t off,
1038 uint64_t cb, bool *pfAllocated)
1039{
1040 RTVFS hVfs = (RTVFS)pvUser;
1041 return RTVfsIsRangeInUse(hVfs, off, cb, pfAllocated);
1042}
1043
1044
1045static DECLCALLBACK(int) vboximgQueryRangeUse(void *pvUser, uint64_t off, uint64_t cb,
1046 bool *pfUsed)
1047{
1048 RTDVM hVolMgr = (RTDVM)pvUser;
1049 return RTDvmMapQueryBlockStatus(hVolMgr, off, cb, pfUsed);
1050}
1051
1052
1053typedef struct VBOXIMGVFS
1054{
1055 /** Pointer to the next VFS handle. */
1056 struct VBOXIMGVFS *pNext;
1057 /** VFS handle. */
1058 RTVFS hVfs;
1059} VBOXIMGVFS, *PVBOXIMGVFS;
1060
1061int handleCompact(HandlerArg *a)
1062{
1063 int rc = VINF_SUCCESS;
1064 PVBOXHDD pDisk = NULL;
1065 const char *pszFilename = NULL;
1066 bool fFilesystemAware = false;
1067 VDINTERFACEQUERYRANGEUSE VDIfQueryRangeUse;
1068 PVDINTERFACE pIfsCompact = NULL;
1069 RTDVM hDvm = NIL_RTDVM;
1070 PVBOXIMGVFS pVBoxImgVfsHead = NULL;
1071
1072 /* Parse the command line. */
1073 static const RTGETOPTDEF s_aOptions[] =
1074 {
1075 { "--filename", 'f', RTGETOPT_REQ_STRING },
1076 { "--filesystemaware", 'a', RTGETOPT_REQ_NOTHING }
1077 };
1078 int ch;
1079 RTGETOPTUNION ValueUnion;
1080 RTGETOPTSTATE GetState;
1081 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1082 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1083 {
1084 switch (ch)
1085 {
1086 case 'f': // --filename
1087 pszFilename = ValueUnion.psz;
1088 break;
1089
1090 case 'a':
1091 fFilesystemAware = true;
1092 break;
1093
1094 default:
1095 ch = RTGetOptPrintError(ch, &ValueUnion);
1096 printUsage(g_pStdErr);
1097 return ch;
1098 }
1099 }
1100
1101 /* Check for mandatory parameters. */
1102 if (!pszFilename)
1103 return errorSyntax("Mandatory --filename option missing\n");
1104
1105 /* just try it */
1106 char *pszFormat = NULL;
1107 VDTYPE enmType = VDTYPE_INVALID;
1108 rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat, &enmType);
1109 if (RT_FAILURE(rc))
1110 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
1111
1112 rc = VDCreate(pVDIfs, enmType, &pDisk);
1113 if (RT_FAILURE(rc))
1114 return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
1115
1116 /* Open the image */
1117 rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
1118 if (RT_FAILURE(rc))
1119 return errorRuntime("Error while opening the image: %Rrc\n", rc);
1120
1121 if ( RT_SUCCESS(rc)
1122 && fFilesystemAware)
1123 {
1124 uint64_t cbDisk = 0;
1125
1126 cbDisk = VDGetSize(pDisk, 0);
1127 if (cbDisk > 0)
1128 {
1129 rc = RTDvmCreate(&hDvm, vboximgDvmRead, vboximgDvmWrite, cbDisk, 512,
1130 0 /* fFlags*/, pDisk);
1131 if (RT_SUCCESS(rc))
1132 {
1133 rc = RTDvmMapOpen(hDvm);
1134 if ( RT_SUCCESS(rc)
1135 && RTDvmMapGetValidVolumes(hDvm))
1136 {
1137 RTDVMVOLUME hVol;
1138
1139 /* Get all volumes and set the block query status callback. */
1140 rc = RTDvmMapQueryFirstVolume(hDvm, &hVol);
1141 AssertRC(rc);
1142
1143 do
1144 {
1145 RTVFSFILE hVfsFile;
1146 RTVFS hVfs;
1147 RTDVMVOLUME hVolNext;
1148
1149 rc = RTDvmVolumeCreateVfsFile(hVol, &hVfsFile);
1150 if (RT_FAILURE(rc))
1151 break;
1152
1153 /* Try to detect the filesystem in this volume. */
1154 rc = RTFilesystemVfsFromFile(hVfsFile, &hVfs);
1155 if (rc == VERR_NOT_SUPPORTED)
1156 {
1157 /* Release the file handle and continue.*/
1158 RTVfsFileRelease(hVfsFile);
1159 }
1160 else if RT_FAILURE(rc)
1161 break;
1162 else
1163 {
1164 PVBOXIMGVFS pVBoxImgVfs = (PVBOXIMGVFS)RTMemAllocZ(sizeof(VBOXIMGVFS));
1165 if (!pVBoxImgVfs)
1166 rc = VERR_NO_MEMORY;
1167 else
1168 {
1169 pVBoxImgVfs->hVfs = hVfs;
1170 pVBoxImgVfs->pNext = pVBoxImgVfsHead;
1171 pVBoxImgVfsHead = pVBoxImgVfs;
1172 RTDvmVolumeSetQueryBlockStatusCallback(hVol, vboximgQueryBlockStatus, hVfs);
1173 }
1174 }
1175
1176 if (RT_SUCCESS(rc))
1177 rc = RTDvmMapQueryNextVolume(hDvm, hVol, &hVolNext);
1178
1179 /*
1180 * Release the volume handle, the file handle has a reference
1181 * to keep it open.
1182 */
1183 RTDvmVolumeRelease(hVol);
1184 hVol = hVolNext;
1185 } while (RT_SUCCESS(rc));
1186
1187 if (rc == VERR_DVM_MAP_NO_VOLUME)
1188 rc = VINF_SUCCESS;
1189
1190 if (RT_SUCCESS(rc))
1191 {
1192 VDIfQueryRangeUse.pfnQueryRangeUse = vboximgQueryRangeUse;
1193 VDInterfaceAdd(&VDIfQueryRangeUse.Core, "QueryRangeUse", VDINTERFACETYPE_QUERYRANGEUSE,
1194 hDvm, sizeof(VDINTERFACEQUERYRANGEUSE), &pIfsCompact);
1195 }
1196 }
1197 else if (RT_SUCCESS(rc))
1198 RTPrintf("There are no partitions in the volume map\n");
1199 else if (rc == VERR_NOT_FOUND)
1200 {
1201 rc = VINF_SUCCESS;
1202 RTPrintf("No known volume format on disk found\n");
1203 }
1204 else
1205 errorRuntime("Error while opening the volume manager: %Rrc\n", rc);
1206 }
1207 else
1208 errorRuntime("Error creating the volume manager: %Rrc\n", rc);
1209 }
1210 else
1211 {
1212 rc = VERR_INVALID_STATE;
1213 errorRuntime("Error while getting the disk size\n");
1214 }
1215 }
1216
1217 if (RT_SUCCESS(rc))
1218 {
1219 rc = VDCompact(pDisk, 0, pIfsCompact);
1220 if (RT_FAILURE(rc))
1221 errorRuntime("Error while compacting image: %Rrc\n", rc);
1222 }
1223
1224 while (pVBoxImgVfsHead)
1225 {
1226 PVBOXIMGVFS pVBoxImgVfsFree = pVBoxImgVfsHead;
1227
1228 pVBoxImgVfsHead = pVBoxImgVfsHead->pNext;
1229 RTVfsRelease(pVBoxImgVfsFree->hVfs);
1230 RTMemFree(pVBoxImgVfsFree);
1231 }
1232
1233 if (hDvm)
1234 RTDvmRelease(hDvm);
1235
1236 VDDestroy(pDisk);
1237
1238 return rc;
1239}
1240
1241
1242int handleCreateCache(HandlerArg *a)
1243{
1244 int rc = VINF_SUCCESS;
1245 PVBOXHDD pDisk = NULL;
1246 const char *pszFilename = NULL;
1247 uint64_t cbSize = 0;
1248
1249 /* Parse the command line. */
1250 static const RTGETOPTDEF s_aOptions[] =
1251 {
1252 { "--filename", 'f', RTGETOPT_REQ_STRING },
1253 { "--size", 's', RTGETOPT_REQ_UINT64 }
1254 };
1255 int ch;
1256 RTGETOPTUNION ValueUnion;
1257 RTGETOPTSTATE GetState;
1258 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1259 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1260 {
1261 switch (ch)
1262 {
1263 case 'f': // --filename
1264 pszFilename = ValueUnion.psz;
1265 break;
1266
1267 case 's': // --size
1268 cbSize = ValueUnion.u64;
1269 break;
1270
1271 default:
1272 ch = RTGetOptPrintError(ch, &ValueUnion);
1273 printUsage(g_pStdErr);
1274 return ch;
1275 }
1276 }
1277
1278 /* Check for mandatory parameters. */
1279 if (!pszFilename)
1280 return errorSyntax("Mandatory --filename option missing\n");
1281
1282 if (!cbSize)
1283 return errorSyntax("Mandatory --size option missing\n");
1284
1285 /* just try it */
1286 rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
1287 if (RT_FAILURE(rc))
1288 return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
1289
1290 rc = VDCreateCache(pDisk, "VCI", pszFilename, cbSize, VD_IMAGE_FLAGS_DEFAULT,
1291 NULL, NULL, VD_OPEN_FLAGS_NORMAL, NULL, NULL);
1292 if (RT_FAILURE(rc))
1293 return errorRuntime("Error while creating the virtual disk cache: %Rrc\n", rc);
1294
1295 VDDestroy(pDisk);
1296
1297 return rc;
1298}
1299
1300static DECLCALLBACK(bool) vdIfCfgCreateBaseAreKeysValid(void *pvUser, const char *pszzValid)
1301{
1302 return VINF_SUCCESS; /** @todo: Implement. */
1303}
1304
1305static DECLCALLBACK(int) vdIfCfgCreateBaseQuerySize(void *pvUser, const char *pszName, size_t *pcbValue)
1306{
1307 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
1308
1309 AssertPtrReturn(pvUser, VERR_GENERAL_FAILURE);
1310
1311 if (RTStrCmp(pszName, "DataAlignment"))
1312 return VERR_CFGM_VALUE_NOT_FOUND;
1313
1314 *pcbValue = strlen((const char *)pvUser) + 1 /* include terminator */;
1315
1316 return VINF_SUCCESS;
1317}
1318
1319static DECLCALLBACK(int) vdIfCfgCreateBaseQuery(void *pvUser, const char *pszName, char *pszValue, size_t cchValue)
1320{
1321 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
1322
1323 AssertPtrReturn(pvUser, VERR_GENERAL_FAILURE);
1324
1325 if (RTStrCmp(pszName, "DataAlignment"))
1326 return VERR_CFGM_VALUE_NOT_FOUND;
1327
1328 if (strlen((const char *)pvUser) >= cchValue)
1329 return VERR_CFGM_NOT_ENOUGH_SPACE;
1330
1331 memcpy(pszValue, pvUser, strlen((const char *)pvUser) + 1);
1332
1333 return VINF_SUCCESS;
1334
1335}
1336
1337int handleCreateBase(HandlerArg *a)
1338{
1339 int rc = VINF_SUCCESS;
1340 PVBOXHDD pDisk = NULL;
1341 const char *pszFilename = NULL;
1342 const char *pszBackend = "VDI";
1343 const char *pszVariant = NULL;
1344 unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
1345 uint64_t cbSize = 0;
1346 const char *pszDataAlignment = NULL;
1347 VDGEOMETRY LCHSGeometry, PCHSGeometry;
1348 PVDINTERFACE pVDIfsOperation = NULL;
1349 VDINTERFACECONFIG vdIfCfg;
1350
1351 memset(&LCHSGeometry, 0, sizeof(VDGEOMETRY));
1352 memset(&PCHSGeometry, 0, sizeof(VDGEOMETRY));
1353
1354 /* Parse the command line. */
1355 static const RTGETOPTDEF s_aOptions[] =
1356 {
1357 { "--filename", 'f', RTGETOPT_REQ_STRING },
1358 { "--size", 's', RTGETOPT_REQ_UINT64 },
1359 { "--format", 'b', RTGETOPT_REQ_STRING },
1360 { "--variant", 'v', RTGETOPT_REQ_STRING },
1361 { "--dataalignment", 'a', RTGETOPT_REQ_STRING }
1362 };
1363 int ch;
1364 RTGETOPTUNION ValueUnion;
1365 RTGETOPTSTATE GetState;
1366 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1367 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1368 {
1369 switch (ch)
1370 {
1371 case 'f': // --filename
1372 pszFilename = ValueUnion.psz;
1373 break;
1374
1375 case 's': // --size
1376 cbSize = ValueUnion.u64;
1377 break;
1378
1379 case 'b': // --format
1380 pszBackend = ValueUnion.psz;
1381 break;
1382
1383 case 'v': // --variant
1384 pszVariant = ValueUnion.psz;
1385 break;
1386
1387 case 'a': // --dataalignment
1388 pszDataAlignment = ValueUnion.psz;
1389 break;
1390
1391 default:
1392 ch = RTGetOptPrintError(ch, &ValueUnion);
1393 printUsage(g_pStdErr);
1394 return ch;
1395 }
1396 }
1397
1398 /* Check for mandatory parameters. */
1399 if (!pszFilename)
1400 return errorSyntax("Mandatory --filename option missing\n");
1401
1402 if (!cbSize)
1403 return errorSyntax("Mandatory --size option missing\n");
1404
1405 if (pszVariant)
1406 {
1407 rc = parseDiskVariant(pszVariant, &uImageFlags);
1408 if (RT_FAILURE(rc))
1409 return errorSyntax("Invalid variant %s given\n", pszVariant);
1410 }
1411
1412 /* Setup the config interface if required. */
1413 if (pszDataAlignment)
1414 {
1415 vdIfCfg.pfnAreKeysValid = vdIfCfgCreateBaseAreKeysValid;
1416 vdIfCfg.pfnQuerySize = vdIfCfgCreateBaseQuerySize;
1417 vdIfCfg.pfnQuery = vdIfCfgCreateBaseQuery;
1418 VDInterfaceAdd(&vdIfCfg.Core, "Config", VDINTERFACETYPE_CONFIG, (void *)pszDataAlignment,
1419 sizeof(vdIfCfg), &pVDIfsOperation);
1420 }
1421
1422 /* just try it */
1423 rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
1424 if (RT_FAILURE(rc))
1425 return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
1426
1427 rc = VDCreateBase(pDisk, pszBackend, pszFilename, cbSize, uImageFlags,
1428 NULL, &PCHSGeometry, &LCHSGeometry, NULL, VD_OPEN_FLAGS_NORMAL,
1429 NULL, pVDIfsOperation);
1430 if (RT_FAILURE(rc))
1431 return errorRuntime("Error while creating the virtual disk: %Rrc\n", rc);
1432
1433 VDDestroy(pDisk);
1434
1435 return rc;
1436}
1437
1438
1439int handleRepair(HandlerArg *a)
1440{
1441 int rc = VINF_SUCCESS;
1442 PVBOXHDD pDisk = NULL;
1443 const char *pszFilename = NULL;
1444 char *pszBackend = NULL;
1445 const char *pszFormat = NULL;
1446 bool fDryRun = false;
1447 VDTYPE enmType = VDTYPE_HDD;
1448
1449 /* Parse the command line. */
1450 static const RTGETOPTDEF s_aOptions[] =
1451 {
1452 { "--filename", 'f', RTGETOPT_REQ_STRING },
1453 { "--dry-run", 'd', RTGETOPT_REQ_NOTHING },
1454 { "--format", 'b', RTGETOPT_REQ_STRING }
1455 };
1456 int ch;
1457 RTGETOPTUNION ValueUnion;
1458 RTGETOPTSTATE GetState;
1459 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1460 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1461 {
1462 switch (ch)
1463 {
1464 case 'f': // --filename
1465 pszFilename = ValueUnion.psz;
1466 break;
1467
1468 case 'd': // --dry-run
1469 fDryRun = true;
1470 break;
1471
1472 case 'b': // --format
1473 pszFormat = ValueUnion.psz;
1474 break;
1475
1476 default:
1477 ch = RTGetOptPrintError(ch, &ValueUnion);
1478 printUsage(g_pStdErr);
1479 return ch;
1480 }
1481 }
1482
1483 /* Check for mandatory parameters. */
1484 if (!pszFilename)
1485 return errorSyntax("Mandatory --filename option missing\n");
1486
1487 /* just try it */
1488 if (!pszFormat)
1489 {
1490 rc = VDGetFormat(NULL, NULL, pszFilename, &pszBackend, &enmType);
1491 if (RT_FAILURE(rc))
1492 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
1493 pszFormat = pszBackend;
1494 }
1495
1496 rc = VDRepair(pVDIfs, NULL, pszFilename, pszFormat, fDryRun ? VD_REPAIR_DRY_RUN : 0);
1497 if (RT_FAILURE(rc))
1498 rc = errorRuntime("Error while repairing the virtual disk: %Rrc\n", rc);
1499
1500 if (pszBackend)
1501 RTStrFree(pszBackend);
1502 return rc;
1503}
1504
1505
1506int main(int argc, char *argv[])
1507{
1508 int exitcode = 0;
1509
1510 int rc = RTR3InitExe(argc, &argv, 0);
1511 if (RT_FAILURE(rc))
1512 return RTMsgInitFailure(rc);
1513
1514 g_pszProgName = RTPathFilename(argv[0]);
1515
1516 bool fShowLogo = false;
1517 int iCmd = 1;
1518 int iCmdArg;
1519
1520 /* global options */
1521 for (int i = 1; i < argc || argc <= iCmd; i++)
1522 {
1523 if ( argc <= iCmd
1524 || !strcmp(argv[i], "help")
1525 || !strcmp(argv[i], "-?")
1526 || !strcmp(argv[i], "-h")
1527 || !strcmp(argv[i], "-help")
1528 || !strcmp(argv[i], "--help"))
1529 {
1530 showLogo(g_pStdOut);
1531 printUsage(g_pStdOut);
1532 return 0;
1533 }
1534
1535 if ( !strcmp(argv[i], "-v")
1536 || !strcmp(argv[i], "-version")
1537 || !strcmp(argv[i], "-Version")
1538 || !strcmp(argv[i], "--version"))
1539 {
1540 /* Print version number, and do nothing else. */
1541 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
1542 return 0;
1543 }
1544
1545 if ( !strcmp(argv[i], "--nologo")
1546 || !strcmp(argv[i], "-nologo")
1547 || !strcmp(argv[i], "-q"))
1548 {
1549 /* suppress the logo */
1550 fShowLogo = false;
1551 iCmd++;
1552 }
1553 else
1554 {
1555 break;
1556 }
1557 }
1558
1559 iCmdArg = iCmd + 1;
1560
1561 if (fShowLogo)
1562 showLogo(g_pStdOut);
1563
1564 /* initialize the VD backend with dummy handlers */
1565 VDINTERFACEERROR vdInterfaceError;
1566 vdInterfaceError.pfnError = handleVDError;
1567 vdInterfaceError.pfnMessage = handleVDMessage;
1568
1569 rc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1570 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
1571
1572 rc = VDInit();
1573 if (RT_FAILURE(rc))
1574 {
1575 errorSyntax("Initializing backends failed! rc=%Rrc\n", rc);
1576 return 1;
1577 }
1578
1579 /*
1580 * All registered command handlers
1581 */
1582 static const struct
1583 {
1584 const char *command;
1585 int (*handler)(HandlerArg *a);
1586 } s_commandHandlers[] =
1587 {
1588 { "setuuid", handleSetUUID },
1589 { "convert", handleConvert },
1590 { "info", handleInfo },
1591 { "compact", handleCompact },
1592 { "createcache", handleCreateCache },
1593 { "createbase", handleCreateBase },
1594 { "repair", handleRepair },
1595 { NULL, NULL }
1596 };
1597
1598 HandlerArg handlerArg = { 0, NULL };
1599 int commandIndex;
1600 for (commandIndex = 0; s_commandHandlers[commandIndex].command != NULL; commandIndex++)
1601 {
1602 if (!strcmp(s_commandHandlers[commandIndex].command, argv[iCmd]))
1603 {
1604 handlerArg.argc = argc - iCmdArg;
1605 handlerArg.argv = &argv[iCmdArg];
1606
1607 exitcode = s_commandHandlers[commandIndex].handler(&handlerArg);
1608 break;
1609 }
1610 }
1611 if (!s_commandHandlers[commandIndex].command)
1612 {
1613 errorSyntax("Invalid command '%s'", argv[iCmd]);
1614 return 1;
1615 }
1616
1617 rc = VDShutdown();
1618 if (RT_FAILURE(rc))
1619 {
1620 errorSyntax("Unloading backends failed! rc=%Rrc\n", rc);
1621 return 1;
1622 }
1623
1624 return exitcode;
1625}
1626
1627/* dummy stub for RuntimeR3 */
1628#ifndef RT_OS_WINDOWS
1629RTDECL(bool) RTAssertShouldPanic(void)
1630{
1631 return true;
1632}
1633#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