VirtualBox

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

Last change on this file since 49439 was 48847, checked in by vboxsync, 11 years ago

Addressing 64-bit windows warnings.

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