VirtualBox

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

Last change on this file since 67047 was 66693, checked in by vboxsync, 8 years ago

vbox-img: Added createfloppy command for testing the FAT12 formatting code.

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette