VirtualBox

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

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

Cosmetic change.

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