VirtualBox

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

Last change on this file since 47786 was 46721, checked in by vboxsync, 11 years ago

vbox-img: Add clearcomment command to reset comments of disk images

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 51.6 KB
Line 
1/* $Id: vbox-img.cpp 46721 2013-06-21 10:54:28Z 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 cbSumRead = 0, cbRead;
437 uint8_t *pTmp = (uint8_t *)&pFS->abBuffer[0];
438 size_t cbTmp = sizeof(pFS->abBuffer);
439 do
440 {
441 rc = RTFileRead(pFS->file, pTmp, cbTmp, &cbRead);
442 if (RT_FAILURE(rc))
443 return rc;
444 pTmp += cbRead;
445 cbTmp -= cbRead;
446 cbSumRead += cbRead;
447 } while (cbTmp && cbRead);
448
449 pFS->offBuffer = 0;
450 pFS->cbBuffer = cbSumRead;
451 if (!cbSumRead && !pcbRead) /* Caller can't handle partial reads. */
452 return VERR_EOF;
453 }
454
455 /* Read several blocks and assemble the result if necessary */
456 size_t cbTotalRead = 0;
457 do
458 {
459 /* Skip over areas no one wants to read. */
460 while (uOffset > pFS->offBuffer + pFS->cbBuffer - 1)
461 {
462 if (pFS->cbBuffer < sizeof(pFS->abBuffer))
463 {
464 if (pcbRead)
465 *pcbRead = cbTotalRead;
466 return VERR_EOF;
467 }
468
469 /* Repeat reading until buffer is full or EOF. */
470 size_t cbSumRead = 0, cbRead;
471 uint8_t *pTmp = (uint8_t *)&pFS->abBuffer[0];
472 size_t cbTmp = sizeof(pFS->abBuffer);
473 do
474 {
475 rc = RTFileRead(pFS->file, pTmp, cbTmp, &cbRead);
476 if (RT_FAILURE(rc))
477 return rc;
478 pTmp += cbRead;
479 cbTmp -= cbRead;
480 cbSumRead += cbRead;
481 } while (cbTmp && cbRead);
482
483 pFS->offBuffer += pFS->cbBuffer;
484 pFS->cbBuffer = cbSumRead;
485 }
486
487 uint32_t cbThisRead = RT_MIN(cbBuffer,
488 pFS->cbBuffer - uOffset % sizeof(pFS->abBuffer));
489 memcpy(pvBuffer, &pFS->abBuffer[uOffset % sizeof(pFS->abBuffer)],
490 cbThisRead);
491 uOffset += cbThisRead;
492 pvBuffer = (uint8_t *)pvBuffer + cbThisRead;
493 cbBuffer -= cbThisRead;
494 cbTotalRead += cbThisRead;
495 if (!cbTotalRead && !pcbRead) /* Caller can't handle partial reads. */
496 return VERR_EOF;
497 } while (cbBuffer > 0);
498
499 if (pcbRead)
500 *pcbRead = cbTotalRead;
501
502 pFS->off = uOffset;
503
504 return VINF_SUCCESS;
505}
506
507static int convInWrite(void *pvUser, void *pStorage, uint64_t uOffset,
508 const void *pvBuffer, size_t cbBuffer,
509 size_t *pcbWritten)
510{
511 NOREF(pvUser);
512 NOREF(pStorage);
513 NOREF(uOffset);
514 NOREF(cbBuffer);
515 NOREF(pcbWritten);
516 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
517 AssertFailedReturn(VERR_NOT_SUPPORTED);
518}
519
520static int convInFlush(void *pvUser, void *pStorage)
521{
522 NOREF(pvUser);
523 NOREF(pStorage);
524 return VINF_SUCCESS;
525}
526
527static int convOutOpen(void *pvUser, const char *pszLocation,
528 uint32_t fOpen, PFNVDCOMPLETED pfnCompleted,
529 void **ppStorage)
530{
531 NOREF(pvUser);
532 /* Validate input. */
533 AssertPtrReturn(ppStorage, VERR_INVALID_POINTER);
534 AssertPtrNullReturn(pfnCompleted, VERR_INVALID_PARAMETER);
535 AssertReturn((fOpen & RTFILE_O_ACCESS_MASK) == RTFILE_O_WRITE, VERR_INVALID_PARAMETER);
536 RTFILE file;
537 int rc = RTFileFromNative(&file, RTFILE_NATIVE_STDOUT);
538 if (RT_FAILURE(rc))
539 return rc;
540
541 /* Must clear buffer, so that skipped over data is initialized properly. */
542 PFILEIOSTATE pFS = (PFILEIOSTATE)RTMemAllocZ(sizeof(FILEIOSTATE));
543 if (!pFS)
544 return VERR_NO_MEMORY;
545
546 pFS->file = file;
547 pFS->off = 0;
548 pFS->offBuffer = 0;
549 pFS->cbBuffer = sizeof(FILEIOSTATE);
550
551 *ppStorage = pFS;
552 return VINF_SUCCESS;
553}
554
555static int convOutClose(void *pvUser, void *pStorage)
556{
557 NOREF(pvUser);
558 AssertPtrReturn(pStorage, VERR_INVALID_POINTER);
559 PFILEIOSTATE pFS = (PFILEIOSTATE)pStorage;
560 int rc = VINF_SUCCESS;
561
562 /* Flush any remaining buffer contents. */
563 if (pFS->cbBuffer)
564 rc = RTFileWrite(pFS->file, &pFS->abBuffer[0], pFS->cbBuffer, NULL);
565
566 RTMemFree(pFS);
567
568 return rc;
569}
570
571static int convOutDelete(void *pvUser, const char *pcszFilename)
572{
573 NOREF(pvUser);
574 NOREF(pcszFilename);
575 AssertFailedReturn(VERR_NOT_SUPPORTED);
576}
577
578static int convOutMove(void *pvUser, const char *pcszSrc, const char *pcszDst,
579 unsigned fMove)
580{
581 NOREF(pvUser);
582 NOREF(pcszSrc);
583 NOREF(pcszDst);
584 NOREF(fMove);
585 AssertFailedReturn(VERR_NOT_SUPPORTED);
586}
587
588static int convOutGetFreeSpace(void *pvUser, const char *pcszFilename,
589 int64_t *pcbFreeSpace)
590{
591 NOREF(pvUser);
592 NOREF(pcszFilename);
593 AssertPtrReturn(pcbFreeSpace, VERR_INVALID_POINTER);
594 *pcbFreeSpace = INT64_MAX;
595 return VINF_SUCCESS;
596}
597
598static int convOutGetModificationTime(void *pvUser, const char *pcszFilename,
599 PRTTIMESPEC pModificationTime)
600{
601 NOREF(pvUser);
602 NOREF(pcszFilename);
603 AssertPtrReturn(pModificationTime, VERR_INVALID_POINTER);
604 AssertFailedReturn(VERR_NOT_SUPPORTED);
605}
606
607static int convOutGetSize(void *pvUser, void *pStorage, uint64_t *pcbSize)
608{
609 NOREF(pvUser);
610 NOREF(pStorage);
611 AssertPtrReturn(pcbSize, VERR_INVALID_POINTER);
612 AssertFailedReturn(VERR_NOT_SUPPORTED);
613}
614
615static int convOutSetSize(void *pvUser, void *pStorage, uint64_t cbSize)
616{
617 NOREF(pvUser);
618 NOREF(pStorage);
619 NOREF(cbSize);
620 AssertFailedReturn(VERR_NOT_SUPPORTED);
621}
622
623static int convOutRead(void *pvUser, void *pStorage, uint64_t uOffset,
624 void *pvBuffer, size_t cbBuffer, size_t *pcbRead)
625{
626 NOREF(pvUser);
627 NOREF(pStorage);
628 NOREF(uOffset);
629 NOREF(cbBuffer);
630 NOREF(pcbRead);
631 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
632 AssertFailedReturn(VERR_NOT_SUPPORTED);
633}
634
635static int convOutWrite(void *pvUser, void *pStorage, uint64_t uOffset,
636 const void *pvBuffer, size_t cbBuffer,
637 size_t *pcbWritten)
638{
639 NOREF(pvUser);
640 AssertPtrReturn(pStorage, VERR_INVALID_POINTER);
641 AssertPtrReturn(pvBuffer, VERR_INVALID_POINTER);
642 PFILEIOSTATE pFS = (PFILEIOSTATE)pStorage;
643 AssertReturn(uOffset >= pFS->off, VERR_INVALID_PARAMETER);
644 int rc;
645
646 /* Write the data to the buffer, flushing as required. */
647 size_t cbTotalWritten = 0;
648 do
649 {
650 /* Flush the buffer if we need a new one. */
651 while (uOffset > pFS->offBuffer + sizeof(pFS->abBuffer) - 1)
652 {
653 rc = RTFileWrite(pFS->file, &pFS->abBuffer[0],
654 sizeof(pFS->abBuffer), NULL);
655 RT_ZERO(pFS->abBuffer);
656 pFS->offBuffer += sizeof(pFS->abBuffer);
657 pFS->cbBuffer = 0;
658 }
659
660 uint32_t cbThisWrite = RT_MIN(cbBuffer,
661 sizeof(pFS->abBuffer) - uOffset % sizeof(pFS->abBuffer));
662 memcpy(&pFS->abBuffer[uOffset % sizeof(pFS->abBuffer)], pvBuffer,
663 cbThisWrite);
664 uOffset += cbThisWrite;
665 pvBuffer = (uint8_t *)pvBuffer + cbThisWrite;
666 cbBuffer -= cbThisWrite;
667 cbTotalWritten += cbThisWrite;
668 } while (cbBuffer > 0);
669
670 if (pcbWritten)
671 *pcbWritten = cbTotalWritten;
672
673 pFS->cbBuffer = uOffset % sizeof(pFS->abBuffer);
674 if (!pFS->cbBuffer)
675 pFS->cbBuffer = sizeof(pFS->abBuffer);
676 pFS->off = uOffset;
677
678 return VINF_SUCCESS;
679}
680
681static int convOutFlush(void *pvUser, void *pStorage)
682{
683 NOREF(pvUser);
684 NOREF(pStorage);
685 return VINF_SUCCESS;
686}
687
688int handleConvert(HandlerArg *a)
689{
690 const char *pszSrcFilename = NULL;
691 const char *pszDstFilename = NULL;
692 bool fStdIn = false;
693 bool fStdOut = false;
694 const char *pszSrcFormat = NULL;
695 VDTYPE enmSrcType = VDTYPE_HDD;
696 const char *pszDstFormat = NULL;
697 const char *pszVariant = NULL;
698 PVBOXHDD pSrcDisk = NULL;
699 PVBOXHDD pDstDisk = NULL;
700 unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
701 PVDINTERFACE pIfsImageInput = NULL;
702 PVDINTERFACE pIfsImageOutput = NULL;
703 VDINTERFACEIO IfsInputIO;
704 VDINTERFACEIO IfsOutputIO;
705 int rc = VINF_SUCCESS;
706
707 /* Parse the command line. */
708 static const RTGETOPTDEF s_aOptions[] =
709 {
710 { "--srcfilename", 'i', RTGETOPT_REQ_STRING },
711 { "--dstfilename", 'o', RTGETOPT_REQ_STRING },
712 { "--stdin", 'p', RTGETOPT_REQ_NOTHING },
713 { "--stdout", 'P', RTGETOPT_REQ_NOTHING },
714 { "--srcformat", 's', RTGETOPT_REQ_STRING },
715 { "--dstformat", 'd', RTGETOPT_REQ_STRING },
716 { "--variant", 'v', RTGETOPT_REQ_STRING }
717 };
718 int ch;
719 RTGETOPTUNION ValueUnion;
720 RTGETOPTSTATE GetState;
721 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
722 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
723 {
724 switch (ch)
725 {
726 case 'i': // --srcfilename
727 pszSrcFilename = ValueUnion.psz;
728 break;
729 case 'o': // --dstfilename
730 pszDstFilename = ValueUnion.psz;
731 break;
732 case 'p': // --stdin
733 fStdIn = true;
734 break;
735 case 'P': // --stdout
736 fStdOut = true;
737 break;
738 case 's': // --srcformat
739 pszSrcFormat = ValueUnion.psz;
740 break;
741 case 'd': // --dstformat
742 pszDstFormat = ValueUnion.psz;
743 break;
744 case 'v': // --variant
745 pszVariant = ValueUnion.psz;
746 break;
747
748 default:
749 ch = RTGetOptPrintError(ch, &ValueUnion);
750 printUsage(g_pStdErr);
751 return ch;
752 }
753 }
754
755 /* Check for mandatory parameters and handle dummies/defaults. */
756 if (fStdIn && !pszSrcFormat)
757 return errorSyntax("Mandatory --srcformat option missing\n");
758 if (!pszDstFormat)
759 pszDstFormat = "VDI";
760 if (fStdIn && !pszSrcFilename)
761 {
762 /* Complete dummy, will be just passed to various calls to fulfill
763 * the "must be non-NULL" requirement, and is completely ignored
764 * otherwise. It shown in the stderr message below. */
765 pszSrcFilename = "stdin";
766 }
767 if (fStdOut && !pszDstFilename)
768 {
769 /* Will be stored in the destination image if it is a streamOptimized
770 * VMDK, but it isn't really relevant - use it for "branding". */
771 if (!RTStrICmp(pszDstFormat, "VMDK"))
772 pszDstFilename = "VirtualBoxStream.vmdk";
773 else
774 pszDstFilename = "stdout";
775 }
776 if (!pszSrcFilename)
777 return errorSyntax("Mandatory --srcfilename option missing\n");
778 if (!pszDstFilename)
779 return errorSyntax("Mandatory --dstfilename option missing\n");
780
781 if (fStdIn)
782 {
783 IfsInputIO.pfnOpen = convInOpen;
784 IfsInputIO.pfnClose = convInClose;
785 IfsInputIO.pfnDelete = convInDelete;
786 IfsInputIO.pfnMove = convInMove;
787 IfsInputIO.pfnGetFreeSpace = convInGetFreeSpace;
788 IfsInputIO.pfnGetModificationTime = convInGetModificationTime;
789 IfsInputIO.pfnGetSize = convInGetSize;
790 IfsInputIO.pfnSetSize = convInSetSize;
791 IfsInputIO.pfnReadSync = convInRead;
792 IfsInputIO.pfnWriteSync = convInWrite;
793 IfsInputIO.pfnFlushSync = convInFlush;
794 VDInterfaceAdd(&IfsInputIO.Core, "stdin", VDINTERFACETYPE_IO,
795 NULL, sizeof(VDINTERFACEIO), &pIfsImageInput);
796 }
797 if (fStdOut)
798 {
799 IfsOutputIO.pfnOpen = convOutOpen;
800 IfsOutputIO.pfnClose = convOutClose;
801 IfsOutputIO.pfnDelete = convOutDelete;
802 IfsOutputIO.pfnMove = convOutMove;
803 IfsOutputIO.pfnGetFreeSpace = convOutGetFreeSpace;
804 IfsOutputIO.pfnGetModificationTime = convOutGetModificationTime;
805 IfsOutputIO.pfnGetSize = convOutGetSize;
806 IfsOutputIO.pfnSetSize = convOutSetSize;
807 IfsOutputIO.pfnReadSync = convOutRead;
808 IfsOutputIO.pfnWriteSync = convOutWrite;
809 IfsOutputIO.pfnFlushSync = convOutFlush;
810 VDInterfaceAdd(&IfsOutputIO.Core, "stdout", VDINTERFACETYPE_IO,
811 NULL, sizeof(VDINTERFACEIO), &pIfsImageOutput);
812 }
813
814 /* check the variant parameter */
815 if (pszVariant)
816 {
817 char *psz = (char*)pszVariant;
818 while (psz && *psz && RT_SUCCESS(rc))
819 {
820 size_t len;
821 const char *pszComma = strchr(psz, ',');
822 if (pszComma)
823 len = pszComma - psz;
824 else
825 len = strlen(psz);
826 if (len > 0)
827 {
828 if (!RTStrNICmp(pszVariant, "standard", len))
829 uImageFlags |= VD_IMAGE_FLAGS_NONE;
830 else if (!RTStrNICmp(pszVariant, "fixed", len))
831 uImageFlags |= VD_IMAGE_FLAGS_FIXED;
832 else if (!RTStrNICmp(pszVariant, "split2g", len))
833 uImageFlags |= VD_VMDK_IMAGE_FLAGS_SPLIT_2G;
834 else if (!RTStrNICmp(pszVariant, "stream", len))
835 uImageFlags |= VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED;
836 else if (!RTStrNICmp(pszVariant, "esx", len))
837 uImageFlags |= VD_VMDK_IMAGE_FLAGS_ESX;
838 else
839 return errorSyntax("Invalid --variant option\n");
840 }
841 if (pszComma)
842 psz += len + 1;
843 else
844 psz += len;
845 }
846 }
847
848 do
849 {
850 /* try to determine input format if not specified */
851 if (!pszSrcFormat)
852 {
853 char *pszFormat = NULL;
854 VDTYPE enmType = VDTYPE_INVALID;
855 rc = VDGetFormat(NULL, NULL, pszSrcFilename, &pszFormat, &enmType);
856 if (RT_FAILURE(rc))
857 {
858 errorSyntax("No file format specified, please specify format: %Rrc\n", rc);
859 break;
860 }
861 pszSrcFormat = pszFormat;
862 enmSrcType = enmType;
863 }
864
865 rc = VDCreate(pVDIfs, enmSrcType, &pSrcDisk);
866 if (RT_FAILURE(rc))
867 {
868 errorRuntime("Error while creating source disk container: %Rrc\n", rc);
869 break;
870 }
871
872 rc = VDOpen(pSrcDisk, pszSrcFormat, pszSrcFilename,
873 VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL,
874 pIfsImageInput);
875 if (RT_FAILURE(rc))
876 {
877 errorRuntime("Error while opening source image: %Rrc\n", rc);
878 break;
879 }
880
881 rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDstDisk);
882 if (RT_FAILURE(rc))
883 {
884 errorRuntime("Error while creating the destination disk container: %Rrc\n", rc);
885 break;
886 }
887
888 uint64_t cbSize = VDGetSize(pSrcDisk, VD_LAST_IMAGE);
889 RTStrmPrintf(g_pStdErr, "Converting image \"%s\" with size %RU64 bytes (%RU64MB)...\n", pszSrcFilename, cbSize, (cbSize + _1M - 1) / _1M);
890
891 /* Create the output image */
892 rc = VDCopy(pSrcDisk, VD_LAST_IMAGE, pDstDisk, pszDstFormat,
893 pszDstFilename, false, 0, uImageFlags, NULL,
894 VD_OPEN_FLAGS_NORMAL | VD_OPEN_FLAGS_SEQUENTIAL, NULL,
895 pIfsImageOutput, NULL);
896 if (RT_FAILURE(rc))
897 {
898 errorRuntime("Error while copying the image: %Rrc\n", rc);
899 break;
900 }
901
902 }
903 while (0);
904
905 if (pDstDisk)
906 VDDestroy(pDstDisk);
907 if (pSrcDisk)
908 VDDestroy(pSrcDisk);
909
910 return RT_SUCCESS(rc) ? 0 : 1;
911}
912
913
914int handleInfo(HandlerArg *a)
915{
916 int rc = VINF_SUCCESS;
917 PVBOXHDD pDisk = NULL;
918 const char *pszFilename = NULL;
919
920 /* Parse the command line. */
921 static const RTGETOPTDEF s_aOptions[] =
922 {
923 { "--filename", 'f', RTGETOPT_REQ_STRING }
924 };
925 int ch;
926 RTGETOPTUNION ValueUnion;
927 RTGETOPTSTATE GetState;
928 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
929 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
930 {
931 switch (ch)
932 {
933 case 'f': // --filename
934 pszFilename = ValueUnion.psz;
935 break;
936
937 default:
938 ch = RTGetOptPrintError(ch, &ValueUnion);
939 printUsage(g_pStdErr);
940 return ch;
941 }
942 }
943
944 /* Check for mandatory parameters. */
945 if (!pszFilename)
946 return errorSyntax("Mandatory --filename option missing\n");
947
948 /* just try it */
949 char *pszFormat = NULL;
950 VDTYPE enmType = VDTYPE_INVALID;
951 rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat, &enmType);
952 if (RT_FAILURE(rc))
953 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
954
955 rc = VDCreate(pVDIfs, enmType, &pDisk);
956 if (RT_FAILURE(rc))
957 return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
958
959 /* Open the image */
960 rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO | VD_OPEN_FLAGS_READONLY, NULL);
961 if (RT_FAILURE(rc))
962 return errorRuntime("Error while opening the image: %Rrc\n", rc);
963
964 VDDumpImages(pDisk);
965
966 VDDestroy(pDisk);
967
968 return rc;
969}
970
971
972static DECLCALLBACK(int) vboximgDvmRead(void *pvUser, uint64_t off, void *pvBuf, size_t cbRead)
973{
974 int rc = VINF_SUCCESS;
975 PVBOXHDD pDisk = (PVBOXHDD)pvUser;
976
977 /* Take shortcut if possible. */
978 if ( off % 512 == 0
979 && cbRead % 512 == 0)
980 rc = VDRead(pDisk, off, pvBuf, cbRead);
981 else
982 {
983 uint8_t *pbBuf = (uint8_t *)pvBuf;
984 uint8_t abBuf[512];
985
986 /* Unaligned access, make it aligned. */
987 if (off % 512 != 0)
988 {
989 uint64_t offAligned = off & ~(uint64_t)(512 - 1);
990 size_t cbToCopy = 512 - (off - offAligned);
991 rc = VDRead(pDisk, offAligned, abBuf, 512);
992 if (RT_SUCCESS(rc))
993 {
994 memcpy(pbBuf, &abBuf[off - offAligned], cbToCopy);
995 pbBuf += cbToCopy;
996 off += cbToCopy;
997 cbRead -= cbToCopy;
998 }
999 }
1000
1001 if ( RT_SUCCESS(rc)
1002 && (cbRead & ~(uint64_t)(512 - 1)))
1003 {
1004 size_t cbReadAligned = cbRead & ~(uint64_t)(512 - 1);
1005
1006 Assert(!(off % 512));
1007 rc = VDRead(pDisk, off, pbBuf, cbReadAligned);
1008 if (RT_SUCCESS(rc))
1009 {
1010 pbBuf += cbReadAligned;
1011 off += cbReadAligned;
1012 cbRead -= cbReadAligned;
1013 }
1014 }
1015
1016 if ( RT_SUCCESS(rc)
1017 && cbRead)
1018 {
1019 Assert(cbRead < 512);
1020 Assert(!(off % 512));
1021
1022 rc = VDRead(pDisk, off, abBuf, 512);
1023 if (RT_SUCCESS(rc))
1024 memcpy(pbBuf, abBuf, cbRead);
1025 }
1026 }
1027
1028 return rc;
1029}
1030
1031
1032static DECLCALLBACK(int) vboximgDvmWrite(void *pvUser, uint64_t off, const void *pvBuf, size_t cbWrite)
1033{
1034 PVBOXHDD pDisk = (PVBOXHDD)pvUser;
1035 return VDWrite(pDisk, off, pvBuf, cbWrite);
1036}
1037
1038
1039static DECLCALLBACK(int) vboximgQueryBlockStatus(void *pvUser, uint64_t off,
1040 uint64_t cb, bool *pfAllocated)
1041{
1042 RTVFS hVfs = (RTVFS)pvUser;
1043 return RTVfsIsRangeInUse(hVfs, off, cb, pfAllocated);
1044}
1045
1046
1047static DECLCALLBACK(int) vboximgQueryRangeUse(void *pvUser, uint64_t off, uint64_t cb,
1048 bool *pfUsed)
1049{
1050 RTDVM hVolMgr = (RTDVM)pvUser;
1051 return RTDvmMapQueryBlockStatus(hVolMgr, off, cb, pfUsed);
1052}
1053
1054
1055typedef struct VBOXIMGVFS
1056{
1057 /** Pointer to the next VFS handle. */
1058 struct VBOXIMGVFS *pNext;
1059 /** VFS handle. */
1060 RTVFS hVfs;
1061} VBOXIMGVFS, *PVBOXIMGVFS;
1062
1063int handleCompact(HandlerArg *a)
1064{
1065 int rc = VINF_SUCCESS;
1066 PVBOXHDD pDisk = NULL;
1067 const char *pszFilename = NULL;
1068 bool fFilesystemAware = false;
1069 VDINTERFACEQUERYRANGEUSE VDIfQueryRangeUse;
1070 PVDINTERFACE pIfsCompact = NULL;
1071 RTDVM hDvm = NIL_RTDVM;
1072 PVBOXIMGVFS pVBoxImgVfsHead = NULL;
1073
1074 /* Parse the command line. */
1075 static const RTGETOPTDEF s_aOptions[] =
1076 {
1077 { "--filename", 'f', RTGETOPT_REQ_STRING },
1078 { "--filesystemaware", 'a', RTGETOPT_REQ_NOTHING }
1079 };
1080 int ch;
1081 RTGETOPTUNION ValueUnion;
1082 RTGETOPTSTATE GetState;
1083 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1084 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1085 {
1086 switch (ch)
1087 {
1088 case 'f': // --filename
1089 pszFilename = ValueUnion.psz;
1090 break;
1091
1092 case 'a':
1093 fFilesystemAware = true;
1094 break;
1095
1096 default:
1097 ch = RTGetOptPrintError(ch, &ValueUnion);
1098 printUsage(g_pStdErr);
1099 return ch;
1100 }
1101 }
1102
1103 /* Check for mandatory parameters. */
1104 if (!pszFilename)
1105 return errorSyntax("Mandatory --filename option missing\n");
1106
1107 /* just try it */
1108 char *pszFormat = NULL;
1109 VDTYPE enmType = VDTYPE_INVALID;
1110 rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat, &enmType);
1111 if (RT_FAILURE(rc))
1112 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
1113
1114 rc = VDCreate(pVDIfs, enmType, &pDisk);
1115 if (RT_FAILURE(rc))
1116 return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
1117
1118 /* Open the image */
1119 rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
1120 if (RT_FAILURE(rc))
1121 return errorRuntime("Error while opening the image: %Rrc\n", rc);
1122
1123 if ( RT_SUCCESS(rc)
1124 && fFilesystemAware)
1125 {
1126 uint64_t cbDisk = 0;
1127
1128 cbDisk = VDGetSize(pDisk, 0);
1129 if (cbDisk > 0)
1130 {
1131 rc = RTDvmCreate(&hDvm, vboximgDvmRead, vboximgDvmWrite, cbDisk, 512,
1132 0 /* fFlags*/, pDisk);
1133 if (RT_SUCCESS(rc))
1134 {
1135 rc = RTDvmMapOpen(hDvm);
1136 if ( RT_SUCCESS(rc)
1137 && RTDvmMapGetValidVolumes(hDvm))
1138 {
1139 RTDVMVOLUME hVol;
1140
1141 /* Get all volumes and set the block query status callback. */
1142 rc = RTDvmMapQueryFirstVolume(hDvm, &hVol);
1143 AssertRC(rc);
1144
1145 do
1146 {
1147 RTVFSFILE hVfsFile;
1148 RTVFS hVfs;
1149 RTDVMVOLUME hVolNext;
1150
1151 rc = RTDvmVolumeCreateVfsFile(hVol, &hVfsFile);
1152 if (RT_FAILURE(rc))
1153 break;
1154
1155 /* Try to detect the filesystem in this volume. */
1156 rc = RTFilesystemVfsFromFile(hVfsFile, &hVfs);
1157 if (rc == VERR_NOT_SUPPORTED)
1158 {
1159 /* Release the file handle and continue.*/
1160 RTVfsFileRelease(hVfsFile);
1161 }
1162 else if RT_FAILURE(rc)
1163 break;
1164 else
1165 {
1166 PVBOXIMGVFS pVBoxImgVfs = (PVBOXIMGVFS)RTMemAllocZ(sizeof(VBOXIMGVFS));
1167 if (!pVBoxImgVfs)
1168 rc = VERR_NO_MEMORY;
1169 else
1170 {
1171 pVBoxImgVfs->hVfs = hVfs;
1172 pVBoxImgVfs->pNext = pVBoxImgVfsHead;
1173 pVBoxImgVfsHead = pVBoxImgVfs;
1174 RTDvmVolumeSetQueryBlockStatusCallback(hVol, vboximgQueryBlockStatus, hVfs);
1175 }
1176 }
1177
1178 if (RT_SUCCESS(rc))
1179 rc = RTDvmMapQueryNextVolume(hDvm, hVol, &hVolNext);
1180
1181 /*
1182 * Release the volume handle, the file handle has a reference
1183 * to keep it open.
1184 */
1185 RTDvmVolumeRelease(hVol);
1186 hVol = hVolNext;
1187 } while (RT_SUCCESS(rc));
1188
1189 if (rc == VERR_DVM_MAP_NO_VOLUME)
1190 rc = VINF_SUCCESS;
1191
1192 if (RT_SUCCESS(rc))
1193 {
1194 VDIfQueryRangeUse.pfnQueryRangeUse = vboximgQueryRangeUse;
1195 VDInterfaceAdd(&VDIfQueryRangeUse.Core, "QueryRangeUse", VDINTERFACETYPE_QUERYRANGEUSE,
1196 hDvm, sizeof(VDINTERFACEQUERYRANGEUSE), &pIfsCompact);
1197 }
1198 }
1199 else if (RT_SUCCESS(rc))
1200 RTPrintf("There are no partitions in the volume map\n");
1201 else if (rc == VERR_NOT_FOUND)
1202 {
1203 rc = VINF_SUCCESS;
1204 RTPrintf("No known volume format on disk found\n");
1205 }
1206 else
1207 errorRuntime("Error while opening the volume manager: %Rrc\n", rc);
1208 }
1209 else
1210 errorRuntime("Error creating the volume manager: %Rrc\n", rc);
1211 }
1212 else
1213 {
1214 rc = VERR_INVALID_STATE;
1215 errorRuntime("Error while getting the disk size\n");
1216 }
1217 }
1218
1219 if (RT_SUCCESS(rc))
1220 {
1221 rc = VDCompact(pDisk, 0, pIfsCompact);
1222 if (RT_FAILURE(rc))
1223 errorRuntime("Error while compacting image: %Rrc\n", rc);
1224 }
1225
1226 while (pVBoxImgVfsHead)
1227 {
1228 PVBOXIMGVFS pVBoxImgVfsFree = pVBoxImgVfsHead;
1229
1230 pVBoxImgVfsHead = pVBoxImgVfsHead->pNext;
1231 RTVfsRelease(pVBoxImgVfsFree->hVfs);
1232 RTMemFree(pVBoxImgVfsFree);
1233 }
1234
1235 if (hDvm)
1236 RTDvmRelease(hDvm);
1237
1238 VDDestroy(pDisk);
1239
1240 return rc;
1241}
1242
1243
1244int handleCreateCache(HandlerArg *a)
1245{
1246 int rc = VINF_SUCCESS;
1247 PVBOXHDD pDisk = NULL;
1248 const char *pszFilename = NULL;
1249 uint64_t cbSize = 0;
1250
1251 /* Parse the command line. */
1252 static const RTGETOPTDEF s_aOptions[] =
1253 {
1254 { "--filename", 'f', RTGETOPT_REQ_STRING },
1255 { "--size", 's', RTGETOPT_REQ_UINT64 }
1256 };
1257 int ch;
1258 RTGETOPTUNION ValueUnion;
1259 RTGETOPTSTATE GetState;
1260 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1261 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1262 {
1263 switch (ch)
1264 {
1265 case 'f': // --filename
1266 pszFilename = ValueUnion.psz;
1267 break;
1268
1269 case 's': // --size
1270 cbSize = ValueUnion.u64;
1271 break;
1272
1273 default:
1274 ch = RTGetOptPrintError(ch, &ValueUnion);
1275 printUsage(g_pStdErr);
1276 return ch;
1277 }
1278 }
1279
1280 /* Check for mandatory parameters. */
1281 if (!pszFilename)
1282 return errorSyntax("Mandatory --filename option missing\n");
1283
1284 if (!cbSize)
1285 return errorSyntax("Mandatory --size option missing\n");
1286
1287 /* just try it */
1288 rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
1289 if (RT_FAILURE(rc))
1290 return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
1291
1292 rc = VDCreateCache(pDisk, "VCI", pszFilename, cbSize, VD_IMAGE_FLAGS_DEFAULT,
1293 NULL, NULL, VD_OPEN_FLAGS_NORMAL, NULL, NULL);
1294 if (RT_FAILURE(rc))
1295 return errorRuntime("Error while creating the virtual disk cache: %Rrc\n", rc);
1296
1297 VDDestroy(pDisk);
1298
1299 return rc;
1300}
1301
1302static DECLCALLBACK(bool) vdIfCfgCreateBaseAreKeysValid(void *pvUser, const char *pszzValid)
1303{
1304 return VINF_SUCCESS; /** @todo: Implement. */
1305}
1306
1307static DECLCALLBACK(int) vdIfCfgCreateBaseQuerySize(void *pvUser, const char *pszName, size_t *pcbValue)
1308{
1309 AssertReturn(VALID_PTR(pcbValue), VERR_INVALID_POINTER);
1310
1311 AssertPtrReturn(pvUser, VERR_GENERAL_FAILURE);
1312
1313 if (RTStrCmp(pszName, "DataAlignment"))
1314 return VERR_CFGM_VALUE_NOT_FOUND;
1315
1316 *pcbValue = strlen((const char *)pvUser) + 1 /* include terminator */;
1317
1318 return VINF_SUCCESS;
1319}
1320
1321static DECLCALLBACK(int) vdIfCfgCreateBaseQuery(void *pvUser, const char *pszName, char *pszValue, size_t cchValue)
1322{
1323 AssertReturn(VALID_PTR(pszValue), VERR_INVALID_POINTER);
1324
1325 AssertPtrReturn(pvUser, VERR_GENERAL_FAILURE);
1326
1327 if (RTStrCmp(pszName, "DataAlignment"))
1328 return VERR_CFGM_VALUE_NOT_FOUND;
1329
1330 if (strlen((const char *)pvUser) >= cchValue)
1331 return VERR_CFGM_NOT_ENOUGH_SPACE;
1332
1333 memcpy(pszValue, pvUser, strlen((const char *)pvUser) + 1);
1334
1335 return VINF_SUCCESS;
1336
1337}
1338
1339int handleCreateBase(HandlerArg *a)
1340{
1341 int rc = VINF_SUCCESS;
1342 PVBOXHDD pDisk = NULL;
1343 const char *pszFilename = NULL;
1344 const char *pszBackend = "VDI";
1345 const char *pszVariant = NULL;
1346 unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
1347 uint64_t cbSize = 0;
1348 const char *pszDataAlignment = NULL;
1349 VDGEOMETRY LCHSGeometry, PCHSGeometry;
1350 PVDINTERFACE pVDIfsOperation = NULL;
1351 VDINTERFACECONFIG vdIfCfg;
1352
1353 memset(&LCHSGeometry, 0, sizeof(VDGEOMETRY));
1354 memset(&PCHSGeometry, 0, sizeof(VDGEOMETRY));
1355
1356 /* Parse the command line. */
1357 static const RTGETOPTDEF s_aOptions[] =
1358 {
1359 { "--filename", 'f', RTGETOPT_REQ_STRING },
1360 { "--size", 's', RTGETOPT_REQ_UINT64 },
1361 { "--format", 'b', RTGETOPT_REQ_STRING },
1362 { "--variant", 'v', RTGETOPT_REQ_STRING },
1363 { "--dataalignment", 'a', RTGETOPT_REQ_STRING }
1364 };
1365 int ch;
1366 RTGETOPTUNION ValueUnion;
1367 RTGETOPTSTATE GetState;
1368 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1369 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1370 {
1371 switch (ch)
1372 {
1373 case 'f': // --filename
1374 pszFilename = ValueUnion.psz;
1375 break;
1376
1377 case 's': // --size
1378 cbSize = ValueUnion.u64;
1379 break;
1380
1381 case 'b': // --format
1382 pszBackend = ValueUnion.psz;
1383 break;
1384
1385 case 'v': // --variant
1386 pszVariant = ValueUnion.psz;
1387 break;
1388
1389 case 'a': // --dataalignment
1390 pszDataAlignment = ValueUnion.psz;
1391 break;
1392
1393 default:
1394 ch = RTGetOptPrintError(ch, &ValueUnion);
1395 printUsage(g_pStdErr);
1396 return ch;
1397 }
1398 }
1399
1400 /* Check for mandatory parameters. */
1401 if (!pszFilename)
1402 return errorSyntax("Mandatory --filename option missing\n");
1403
1404 if (!cbSize)
1405 return errorSyntax("Mandatory --size option missing\n");
1406
1407 if (pszVariant)
1408 {
1409 rc = parseDiskVariant(pszVariant, &uImageFlags);
1410 if (RT_FAILURE(rc))
1411 return errorSyntax("Invalid variant %s given\n", pszVariant);
1412 }
1413
1414 /* Setup the config interface if required. */
1415 if (pszDataAlignment)
1416 {
1417 vdIfCfg.pfnAreKeysValid = vdIfCfgCreateBaseAreKeysValid;
1418 vdIfCfg.pfnQuerySize = vdIfCfgCreateBaseQuerySize;
1419 vdIfCfg.pfnQuery = vdIfCfgCreateBaseQuery;
1420 VDInterfaceAdd(&vdIfCfg.Core, "Config", VDINTERFACETYPE_CONFIG, (void *)pszDataAlignment,
1421 sizeof(vdIfCfg), &pVDIfsOperation);
1422 }
1423
1424 /* just try it */
1425 rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
1426 if (RT_FAILURE(rc))
1427 return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
1428
1429 rc = VDCreateBase(pDisk, pszBackend, pszFilename, cbSize, uImageFlags,
1430 NULL, &PCHSGeometry, &LCHSGeometry, NULL, VD_OPEN_FLAGS_NORMAL,
1431 NULL, pVDIfsOperation);
1432 if (RT_FAILURE(rc))
1433 return errorRuntime("Error while creating the virtual disk: %Rrc\n", rc);
1434
1435 VDDestroy(pDisk);
1436
1437 return rc;
1438}
1439
1440
1441int handleRepair(HandlerArg *a)
1442{
1443 int rc = VINF_SUCCESS;
1444 PVBOXHDD pDisk = NULL;
1445 const char *pszFilename = NULL;
1446 char *pszBackend = NULL;
1447 const char *pszFormat = NULL;
1448 bool fDryRun = false;
1449 VDTYPE enmType = VDTYPE_HDD;
1450
1451 /* Parse the command line. */
1452 static const RTGETOPTDEF s_aOptions[] =
1453 {
1454 { "--filename", 'f', RTGETOPT_REQ_STRING },
1455 { "--dry-run", 'd', RTGETOPT_REQ_NOTHING },
1456 { "--format", 'b', RTGETOPT_REQ_STRING }
1457 };
1458 int ch;
1459 RTGETOPTUNION ValueUnion;
1460 RTGETOPTSTATE GetState;
1461 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1462 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1463 {
1464 switch (ch)
1465 {
1466 case 'f': // --filename
1467 pszFilename = ValueUnion.psz;
1468 break;
1469
1470 case 'd': // --dry-run
1471 fDryRun = true;
1472 break;
1473
1474 case 'b': // --format
1475 pszFormat = ValueUnion.psz;
1476 break;
1477
1478 default:
1479 ch = RTGetOptPrintError(ch, &ValueUnion);
1480 printUsage(g_pStdErr);
1481 return ch;
1482 }
1483 }
1484
1485 /* Check for mandatory parameters. */
1486 if (!pszFilename)
1487 return errorSyntax("Mandatory --filename option missing\n");
1488
1489 /* just try it */
1490 if (!pszFormat)
1491 {
1492 rc = VDGetFormat(NULL, NULL, pszFilename, &pszBackend, &enmType);
1493 if (RT_FAILURE(rc))
1494 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
1495 pszFormat = pszBackend;
1496 }
1497
1498 rc = VDRepair(pVDIfs, NULL, pszFilename, pszFormat, fDryRun ? VD_REPAIR_DRY_RUN : 0);
1499 if (RT_FAILURE(rc))
1500 rc = errorRuntime("Error while repairing the virtual disk: %Rrc\n", rc);
1501
1502 if (pszBackend)
1503 RTStrFree(pszBackend);
1504 return rc;
1505}
1506
1507
1508int handleClearComment(HandlerArg *a)
1509{
1510 int rc = VINF_SUCCESS;
1511 PVBOXHDD pDisk = NULL;
1512 const char *pszFilename = NULL;
1513 bool fDryRun = false;
1514
1515 /* Parse the command line. */
1516 static const RTGETOPTDEF s_aOptions[] =
1517 {
1518 { "--filename", 'f', RTGETOPT_REQ_STRING }
1519 };
1520 int ch;
1521 RTGETOPTUNION ValueUnion;
1522 RTGETOPTSTATE GetState;
1523 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1524 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1525 {
1526 switch (ch)
1527 {
1528 case 'f': // --filename
1529 pszFilename = ValueUnion.psz;
1530 break;
1531
1532 default:
1533 ch = RTGetOptPrintError(ch, &ValueUnion);
1534 printUsage(g_pStdErr);
1535 return ch;
1536 }
1537 }
1538
1539 /* Check for mandatory parameters. */
1540 if (!pszFilename)
1541 return errorSyntax("Mandatory --filename option missing\n");
1542
1543 /* just try it */
1544 char *pszFormat = NULL;
1545 VDTYPE enmType = VDTYPE_INVALID;
1546 rc = VDGetFormat(NULL, NULL, pszFilename, &pszFormat, &enmType);
1547 if (RT_FAILURE(rc))
1548 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
1549
1550 rc = VDCreate(pVDIfs, enmType, &pDisk);
1551 if (RT_FAILURE(rc))
1552 return errorRuntime("Error while creating the virtual disk container: %Rrc\n", rc);
1553
1554 /* Open the image */
1555 rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO, NULL);
1556 if (RT_FAILURE(rc))
1557 return errorRuntime("Error while opening the image: %Rrc\n", rc);
1558
1559 VDSetComment(pDisk, 0, NULL);
1560
1561 VDDestroy(pDisk);
1562 return rc;
1563}
1564
1565
1566int main(int argc, char *argv[])
1567{
1568 int exitcode = 0;
1569
1570 int rc = RTR3InitExe(argc, &argv, 0);
1571 if (RT_FAILURE(rc))
1572 return RTMsgInitFailure(rc);
1573
1574 g_pszProgName = RTPathFilename(argv[0]);
1575
1576 bool fShowLogo = false;
1577 int iCmd = 1;
1578 int iCmdArg;
1579
1580 /* global options */
1581 for (int i = 1; i < argc || argc <= iCmd; i++)
1582 {
1583 if ( argc <= iCmd
1584 || !strcmp(argv[i], "help")
1585 || !strcmp(argv[i], "-?")
1586 || !strcmp(argv[i], "-h")
1587 || !strcmp(argv[i], "-help")
1588 || !strcmp(argv[i], "--help"))
1589 {
1590 showLogo(g_pStdOut);
1591 printUsage(g_pStdOut);
1592 return 0;
1593 }
1594
1595 if ( !strcmp(argv[i], "-v")
1596 || !strcmp(argv[i], "-version")
1597 || !strcmp(argv[i], "-Version")
1598 || !strcmp(argv[i], "--version"))
1599 {
1600 /* Print version number, and do nothing else. */
1601 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
1602 return 0;
1603 }
1604
1605 if ( !strcmp(argv[i], "--nologo")
1606 || !strcmp(argv[i], "-nologo")
1607 || !strcmp(argv[i], "-q"))
1608 {
1609 /* suppress the logo */
1610 fShowLogo = false;
1611 iCmd++;
1612 }
1613 else
1614 {
1615 break;
1616 }
1617 }
1618
1619 iCmdArg = iCmd + 1;
1620
1621 if (fShowLogo)
1622 showLogo(g_pStdOut);
1623
1624 /* initialize the VD backend with dummy handlers */
1625 VDINTERFACEERROR vdInterfaceError;
1626 vdInterfaceError.pfnError = handleVDError;
1627 vdInterfaceError.pfnMessage = handleVDMessage;
1628
1629 rc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1630 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
1631
1632 rc = VDInit();
1633 if (RT_FAILURE(rc))
1634 {
1635 errorSyntax("Initializing backends failed! rc=%Rrc\n", rc);
1636 return 1;
1637 }
1638
1639 /*
1640 * All registered command handlers
1641 */
1642 static const struct
1643 {
1644 const char *command;
1645 int (*handler)(HandlerArg *a);
1646 } s_commandHandlers[] =
1647 {
1648 { "setuuid", handleSetUUID },
1649 { "convert", handleConvert },
1650 { "info", handleInfo },
1651 { "compact", handleCompact },
1652 { "createcache", handleCreateCache },
1653 { "createbase", handleCreateBase },
1654 { "repair", handleRepair },
1655 { "clearcomment", handleClearComment },
1656 { NULL, NULL }
1657 };
1658
1659 HandlerArg handlerArg = { 0, NULL };
1660 int commandIndex;
1661 for (commandIndex = 0; s_commandHandlers[commandIndex].command != NULL; commandIndex++)
1662 {
1663 if (!strcmp(s_commandHandlers[commandIndex].command, argv[iCmd]))
1664 {
1665 handlerArg.argc = argc - iCmdArg;
1666 handlerArg.argv = &argv[iCmdArg];
1667
1668 exitcode = s_commandHandlers[commandIndex].handler(&handlerArg);
1669 break;
1670 }
1671 }
1672 if (!s_commandHandlers[commandIndex].command)
1673 {
1674 errorSyntax("Invalid command '%s'", argv[iCmd]);
1675 return 1;
1676 }
1677
1678 rc = VDShutdown();
1679 if (RT_FAILURE(rc))
1680 {
1681 errorSyntax("Unloading backends failed! rc=%Rrc\n", rc);
1682 return 1;
1683 }
1684
1685 return exitcode;
1686}
1687
1688/* dummy stub for RuntimeR3 */
1689#ifndef RT_OS_WINDOWS
1690RTDECL(bool) RTAssertShouldPanic(void)
1691{
1692 return true;
1693}
1694#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