VirtualBox

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

Last change on this file since 41550 was 41550, checked in by vboxsync, 13 years ago

vbox-img: New --filesystemaware option for compacting images. Uses filesystem structures on the disk to determine which parts are in use and which not improving the compaction result

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