VirtualBox

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

Last change on this file since 67524 was 67349, checked in by vboxsync, 7 years ago

vbox-img: Added 'createiso' command.

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