VirtualBox

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

最後變更 在這個檔案從96399是 96399,由 vboxsync 提交於 3 年 前

/Config.kmk and many other places: Change VBOX_VENDOR to the official copyright holder text, needs follow-up changes and equivalent adjustments elsewhere.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 70.6 KB
 
1/* $Id: vbox-img.cpp 96399 2022-08-22 14:47:39Z vboxsync $ */
2/** @file
3 * Standalone image manipulation tool
4 */
5
6/*
7 * Copyright (C) 2010-2022 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.alldomusa.eu.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/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 " createiso [too-many-options]\n"
95 "\n"
96 " repair --filename <filename>\n"
97 " [--dry-run]\n"
98 " [--format VDI|VMDK|VHD] (default: autodetect)\n"
99 "\n"
100 " clearcomment --filename <filename>\n"
101 "\n"
102 " resize --filename <filename>\n"
103 " --size <new size>\n",
104 g_pszProgName);
105}
106
107static void showLogo(PRTSTREAM pStrm)
108{
109 static bool s_fShown; /* show only once */
110
111 if (!s_fShown)
112 {
113 RTStrmPrintf(pStrm, VBOX_PRODUCT " Disk Utility " VBOX_VERSION_STRING "\n"
114 "Copyright (C) 2005-" VBOX_C_YEAR " " VBOX_VENDOR "\n\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, VDTYPE_INVALID, &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, VDTYPE_INVALID, &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, VDTYPE_INVALID, &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, VDTYPE_INVALID, &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) vboximgQueryBlockStatus(void *pvUser, uint64_t off,
1334 uint64_t cb, bool *pfAllocated)
1335{
1336 RTVFS hVfs = (RTVFS)pvUser;
1337 return RTVfsQueryRangeState(hVfs, off, cb, pfAllocated);
1338}
1339
1340
1341static DECLCALLBACK(int) vboximgQueryRangeUse(void *pvUser, uint64_t off, uint64_t cb,
1342 bool *pfUsed)
1343{
1344 RTDVM hVolMgr = (RTDVM)pvUser;
1345 return RTDvmMapQueryBlockStatus(hVolMgr, off, cb, pfUsed);
1346}
1347
1348
1349typedef struct VBOXIMGVFS
1350{
1351 /** Pointer to the next VFS handle. */
1352 struct VBOXIMGVFS *pNext;
1353 /** VFS handle. */
1354 RTVFS hVfs;
1355} VBOXIMGVFS, *PVBOXIMGVFS;
1356
1357static int handleCompact(HandlerArg *a)
1358{
1359 PVDISK pDisk = NULL;
1360 VDINTERFACEQUERYRANGEUSE VDIfQueryRangeUse;
1361 PVDINTERFACE pIfsCompact = NULL;
1362 RTDVM hDvm = NIL_RTDVM;
1363 PVBOXIMGVFS pVBoxImgVfsHead = NULL;
1364
1365 /* Parse the command line. */
1366 static const RTGETOPTDEF s_aOptions[] =
1367 {
1368 { "--filename", 'f', RTGETOPT_REQ_STRING },
1369 { "--filesystemaware", 'a', RTGETOPT_REQ_NOTHING },
1370 { "--file-system-aware", 'a', RTGETOPT_REQ_NOTHING },
1371 };
1372
1373 const char *pszFilename = NULL;
1374 bool fFilesystemAware = false;
1375 bool fVerbose = true;
1376
1377 int ch;
1378 RTGETOPTUNION ValueUnion;
1379 RTGETOPTSTATE GetState;
1380 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1381 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1382 {
1383 switch (ch)
1384 {
1385 case 'f': // --filename
1386 pszFilename = ValueUnion.psz;
1387 break;
1388
1389 case 'a':
1390 fFilesystemAware = true;
1391 break;
1392
1393 default:
1394 ch = RTGetOptPrintError(ch, &ValueUnion);
1395 printUsage(g_pStdErr);
1396 return ch;
1397 }
1398 }
1399
1400 /* Check for mandatory parameters. */
1401 if (!pszFilename)
1402 return errorSyntax("Mandatory --filename option missing\n");
1403
1404 /* just try it */
1405 char *pszFormat = NULL;
1406 VDTYPE enmType = VDTYPE_INVALID;
1407 int rc = VDGetFormat(NULL, NULL, pszFilename, VDTYPE_INVALID, &pszFormat, &enmType);
1408 if (RT_FAILURE(rc))
1409 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
1410
1411 rc = VDCreate(pVDIfs, enmType, &pDisk);
1412 if (RT_FAILURE(rc))
1413 return errorRuntime("Error while creating the virtual disk container: %Rrf (%Rrc)\n", rc, rc);
1414
1415 /* Open the image */
1416 rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
1417 RTStrFree(pszFormat);
1418 if (RT_FAILURE(rc))
1419 return errorRuntime("Error while opening the image: %Rrf (%Rrc)\n", rc, rc);
1420
1421 /*
1422 * If --file-system-aware, we first ask the disk volume manager (DVM) to
1423 * find the volumes on the disk.
1424 */
1425 if ( RT_SUCCESS(rc)
1426 && fFilesystemAware)
1427 {
1428 RTVFSFILE hVfsDisk;
1429 rc = VDCreateVfsFileFromDisk(pDisk, 0 /*fFlags*/, &hVfsDisk);
1430 if (RT_SUCCESS(rc))
1431 {
1432 rc = RTDvmCreate(&hDvm, hVfsDisk, 512 /*cbSector*/, 0 /*fFlags*/);
1433 RTVfsFileRelease(hVfsDisk);
1434 if (RT_SUCCESS(rc))
1435 {
1436 rc = RTDvmMapOpen(hDvm);
1437 if ( RT_SUCCESS(rc)
1438 && RTDvmMapGetValidVolumes(hDvm) > 0)
1439 {
1440 /*
1441 * Enumerate the volumes: Try finding a file system interpreter and
1442 * set the block query status callback to work with the FS.
1443 */
1444 uint32_t iVol = 0;
1445 RTDVMVOLUME hVol;
1446 rc = RTDvmMapQueryFirstVolume(hDvm, &hVol);
1447 AssertRC(rc);
1448
1449 while (RT_SUCCESS(rc))
1450 {
1451 if (fVerbose)
1452 {
1453 char *pszVolName;
1454 rc = RTDvmVolumeQueryName(hVol, &pszVolName);
1455 if (RT_FAILURE(rc))
1456 pszVolName = NULL;
1457 RTMsgInfo("Vol%u: %Rhcb %s%s%s\n", iVol, RTDvmVolumeGetSize(hVol),
1458 RTDvmVolumeTypeGetDescr(RTDvmVolumeGetType(hVol)),
1459 pszVolName ? " " : "", pszVolName ? pszVolName : "");
1460 RTStrFree(pszVolName);
1461 }
1462
1463 RTVFSFILE hVfsFile;
1464 rc = RTDvmVolumeCreateVfsFile(hVol, RTFILE_O_READWRITE, &hVfsFile);
1465 if (RT_FAILURE(rc))
1466 {
1467 errorRuntime("RTDvmVolumeCreateVfsFile failed: %Rrc\n");
1468 break;
1469 }
1470
1471 /* Try to detect the filesystem in this volume. */
1472 RTERRINFOSTATIC ErrInfo;
1473 RTVFS hVfs;
1474 rc = RTVfsMountVol(hVfsFile, RTVFSMNT_F_READ_ONLY | RTVFSMNT_F_FOR_RANGE_IN_USE, &hVfs,
1475 RTErrInfoInitStatic(&ErrInfo));
1476 RTVfsFileRelease(hVfsFile);
1477 if (RT_SUCCESS(rc))
1478 {
1479 PVBOXIMGVFS pVBoxImgVfs = (PVBOXIMGVFS)RTMemAllocZ(sizeof(VBOXIMGVFS));
1480 if (!pVBoxImgVfs)
1481 {
1482 RTVfsRelease(hVfs);
1483 rc = VERR_NO_MEMORY;
1484 break;
1485 }
1486 pVBoxImgVfs->hVfs = hVfs;
1487 pVBoxImgVfs->pNext = pVBoxImgVfsHead;
1488 pVBoxImgVfsHead = pVBoxImgVfs;
1489 RTDvmVolumeSetQueryBlockStatusCallback(hVol, vboximgQueryBlockStatus, hVfs);
1490 }
1491 else if (rc != VERR_NOT_SUPPORTED)
1492 {
1493 if (RTErrInfoIsSet(&ErrInfo.Core))
1494 errorRuntime("RTVfsMountVol failed: %s\n", ErrInfo.Core.pszMsg);
1495 break;
1496 }
1497 else if (fVerbose && RTErrInfoIsSet(&ErrInfo.Core))
1498 RTMsgInfo("Unsupported file system: %s", ErrInfo.Core.pszMsg);
1499
1500 /*
1501 * Advance. (Releasing hVol here is fine since RTDvmVolumeCreateVfsFile
1502 * retained a reference and the hVfs a reference of it again.)
1503 */
1504 RTDVMVOLUME hVolNext = NIL_RTDVMVOLUME;
1505 if (RT_SUCCESS(rc))
1506 rc = RTDvmMapQueryNextVolume(hDvm, hVol, &hVolNext);
1507 RTDvmVolumeRelease(hVol);
1508 hVol = hVolNext;
1509 iVol++;
1510 }
1511
1512 if (rc == VERR_DVM_MAP_NO_VOLUME)
1513 rc = VINF_SUCCESS;
1514
1515 if (RT_SUCCESS(rc))
1516 {
1517 VDIfQueryRangeUse.pfnQueryRangeUse = vboximgQueryRangeUse;
1518 VDInterfaceAdd(&VDIfQueryRangeUse.Core, "QueryRangeUse", VDINTERFACETYPE_QUERYRANGEUSE,
1519 hDvm, sizeof(VDINTERFACEQUERYRANGEUSE), &pIfsCompact);
1520 }
1521 }
1522 else if (RT_SUCCESS(rc))
1523 RTPrintf("There are no partitions in the volume map\n");
1524 else if (rc == VERR_NOT_FOUND)
1525 {
1526 RTPrintf("No known volume format on disk found\n");
1527 rc = VINF_SUCCESS;
1528 }
1529 else
1530 errorRuntime("Error while opening the volume manager: %Rrf (%Rrc)\n", rc, rc);
1531 }
1532 else
1533 errorRuntime("Error creating the volume manager: %Rrf (%Rrc)\n", rc, rc);
1534 }
1535 else
1536 errorRuntime("Error while creating VFS interface for the disk: %Rrf (%Rrc)\n", rc, rc);
1537 }
1538
1539 if (RT_SUCCESS(rc))
1540 {
1541 rc = VDCompact(pDisk, 0, pIfsCompact);
1542 if (RT_FAILURE(rc))
1543 errorRuntime("Error while compacting image: %Rrf (%Rrc)\n", rc, rc);
1544 }
1545
1546 while (pVBoxImgVfsHead)
1547 {
1548 PVBOXIMGVFS pVBoxImgVfsFree = pVBoxImgVfsHead;
1549
1550 pVBoxImgVfsHead = pVBoxImgVfsHead->pNext;
1551 RTVfsRelease(pVBoxImgVfsFree->hVfs);
1552 RTMemFree(pVBoxImgVfsFree);
1553 }
1554
1555 if (hDvm)
1556 RTDvmRelease(hDvm);
1557
1558 VDDestroy(pDisk);
1559
1560 return rc;
1561}
1562
1563
1564static int handleCreateCache(HandlerArg *a)
1565{
1566 int rc = VINF_SUCCESS;
1567 PVDISK pDisk = NULL;
1568 const char *pszFilename = NULL;
1569 uint64_t cbSize = 0;
1570
1571 /* Parse the command line. */
1572 static const RTGETOPTDEF s_aOptions[] =
1573 {
1574 { "--filename", 'f', RTGETOPT_REQ_STRING },
1575 { "--size", 's', RTGETOPT_REQ_UINT64 }
1576 };
1577 int ch;
1578 RTGETOPTUNION ValueUnion;
1579 RTGETOPTSTATE GetState;
1580 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1581 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1582 {
1583 switch (ch)
1584 {
1585 case 'f': // --filename
1586 pszFilename = ValueUnion.psz;
1587 break;
1588
1589 case 's': // --size
1590 cbSize = ValueUnion.u64;
1591 break;
1592
1593 default:
1594 ch = RTGetOptPrintError(ch, &ValueUnion);
1595 printUsage(g_pStdErr);
1596 return ch;
1597 }
1598 }
1599
1600 /* Check for mandatory parameters. */
1601 if (!pszFilename)
1602 return errorSyntax("Mandatory --filename option missing\n");
1603
1604 if (!cbSize)
1605 return errorSyntax("Mandatory --size option missing\n");
1606
1607 /* just try it */
1608 rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
1609 if (RT_FAILURE(rc))
1610 return errorRuntime("Error while creating the virtual disk container: %Rrf (%Rrc)\n", rc, rc);
1611
1612 rc = VDCreateCache(pDisk, "VCI", pszFilename, cbSize, VD_IMAGE_FLAGS_DEFAULT,
1613 NULL, NULL, VD_OPEN_FLAGS_NORMAL, NULL, NULL);
1614 if (RT_FAILURE(rc))
1615 return errorRuntime("Error while creating the virtual disk cache: %Rrf (%Rrc)\n", rc, rc);
1616
1617 VDDestroy(pDisk);
1618
1619 return rc;
1620}
1621
1622static DECLCALLBACK(bool) vdIfCfgCreateBaseAreKeysValid(void *pvUser, const char *pszzValid)
1623{
1624 RT_NOREF2(pvUser, pszzValid);
1625 return VINF_SUCCESS; /** @todo Implement. */
1626}
1627
1628static DECLCALLBACK(int) vdIfCfgCreateBaseQuerySize(void *pvUser, const char *pszName, size_t *pcbValue)
1629{
1630 AssertPtrReturn(pcbValue, VERR_INVALID_POINTER);
1631
1632 AssertPtrReturn(pvUser, VERR_GENERAL_FAILURE);
1633
1634 if (RTStrCmp(pszName, "DataAlignment"))
1635 return VERR_CFGM_VALUE_NOT_FOUND;
1636
1637 *pcbValue = strlen((const char *)pvUser) + 1 /* include terminator */;
1638
1639 return VINF_SUCCESS;
1640}
1641
1642static DECLCALLBACK(int) vdIfCfgCreateBaseQuery(void *pvUser, const char *pszName, char *pszValue, size_t cchValue)
1643{
1644 AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
1645
1646 AssertPtrReturn(pvUser, VERR_GENERAL_FAILURE);
1647
1648 if (RTStrCmp(pszName, "DataAlignment"))
1649 return VERR_CFGM_VALUE_NOT_FOUND;
1650
1651 if (strlen((const char *)pvUser) >= cchValue)
1652 return VERR_CFGM_NOT_ENOUGH_SPACE;
1653
1654 memcpy(pszValue, pvUser, strlen((const char *)pvUser) + 1);
1655
1656 return VINF_SUCCESS;
1657
1658}
1659
1660static int handleCreateBase(HandlerArg *a)
1661{
1662 int rc = VINF_SUCCESS;
1663 PVDISK pDisk = NULL;
1664 const char *pszFilename = NULL;
1665 const char *pszBackend = "VDI";
1666 const char *pszVariant = NULL;
1667 unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
1668 uint64_t cbSize = 0;
1669 const char *pszDataAlignment = NULL;
1670 VDGEOMETRY LCHSGeometry, PCHSGeometry;
1671 PVDINTERFACE pVDIfsOperation = NULL;
1672 VDINTERFACECONFIG vdIfCfg;
1673
1674 memset(&LCHSGeometry, 0, sizeof(LCHSGeometry));
1675 memset(&PCHSGeometry, 0, sizeof(PCHSGeometry));
1676
1677 /* Parse the command line. */
1678 static const RTGETOPTDEF s_aOptions[] =
1679 {
1680 { "--filename", 'f', RTGETOPT_REQ_STRING },
1681 { "--size", 's', RTGETOPT_REQ_UINT64 },
1682 { "--format", 'b', RTGETOPT_REQ_STRING },
1683 { "--variant", 'v', RTGETOPT_REQ_STRING },
1684 { "--dataalignment", 'a', RTGETOPT_REQ_STRING }
1685 };
1686 int ch;
1687 RTGETOPTUNION ValueUnion;
1688 RTGETOPTSTATE GetState;
1689 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1690 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1691 {
1692 switch (ch)
1693 {
1694 case 'f': // --filename
1695 pszFilename = ValueUnion.psz;
1696 break;
1697
1698 case 's': // --size
1699 cbSize = ValueUnion.u64;
1700 break;
1701
1702 case 'b': // --format
1703 pszBackend = ValueUnion.psz;
1704 break;
1705
1706 case 'v': // --variant
1707 pszVariant = ValueUnion.psz;
1708 break;
1709
1710 case 'a': // --dataalignment
1711 pszDataAlignment = ValueUnion.psz;
1712 break;
1713
1714 default:
1715 ch = RTGetOptPrintError(ch, &ValueUnion);
1716 printUsage(g_pStdErr);
1717 return ch;
1718 }
1719 }
1720
1721 /* Check for mandatory parameters. */
1722 if (!pszFilename)
1723 return errorSyntax("Mandatory --filename option missing\n");
1724
1725 if (!cbSize)
1726 return errorSyntax("Mandatory --size option missing\n");
1727
1728 if (pszVariant)
1729 {
1730 rc = parseDiskVariant(pszVariant, &uImageFlags);
1731 if (RT_FAILURE(rc))
1732 return errorSyntax("Invalid variant %s given\n", pszVariant);
1733 }
1734
1735 /* Setup the config interface if required. */
1736 if (pszDataAlignment)
1737 {
1738 vdIfCfg.pfnAreKeysValid = vdIfCfgCreateBaseAreKeysValid;
1739 vdIfCfg.pfnQuerySize = vdIfCfgCreateBaseQuerySize;
1740 vdIfCfg.pfnQuery = vdIfCfgCreateBaseQuery;
1741 VDInterfaceAdd(&vdIfCfg.Core, "Config", VDINTERFACETYPE_CONFIG, (void *)pszDataAlignment,
1742 sizeof(vdIfCfg), &pVDIfsOperation);
1743 }
1744
1745 /* just try it */
1746 rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
1747 if (RT_FAILURE(rc))
1748 return errorRuntime("Error while creating the virtual disk container: %Rrf (%Rrc)\n", rc, rc);
1749
1750 rc = VDCreateBase(pDisk, pszBackend, pszFilename, cbSize, uImageFlags,
1751 NULL, &PCHSGeometry, &LCHSGeometry, NULL, VD_OPEN_FLAGS_NORMAL,
1752 NULL, pVDIfsOperation);
1753 if (RT_FAILURE(rc))
1754 return errorRuntime("Error while creating the virtual disk: %Rrf (%Rrc)\n", rc, rc);
1755
1756 VDDestroy(pDisk);
1757
1758 return rc;
1759}
1760
1761
1762static int handleRepair(HandlerArg *a)
1763{
1764 int rc = VINF_SUCCESS;
1765 const char *pszFilename = NULL;
1766 char *pszBackend = NULL;
1767 const char *pszFormat = NULL;
1768 bool fDryRun = false;
1769 VDTYPE enmType = VDTYPE_HDD;
1770
1771 /* Parse the command line. */
1772 static const RTGETOPTDEF s_aOptions[] =
1773 {
1774 { "--filename", 'f', RTGETOPT_REQ_STRING },
1775 { "--dry-run", 'd', RTGETOPT_REQ_NOTHING },
1776 { "--format", 'b', RTGETOPT_REQ_STRING }
1777 };
1778 int ch;
1779 RTGETOPTUNION ValueUnion;
1780 RTGETOPTSTATE GetState;
1781 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1782 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1783 {
1784 switch (ch)
1785 {
1786 case 'f': // --filename
1787 pszFilename = ValueUnion.psz;
1788 break;
1789
1790 case 'd': // --dry-run
1791 fDryRun = true;
1792 break;
1793
1794 case 'b': // --format
1795 pszFormat = ValueUnion.psz;
1796 break;
1797
1798 default:
1799 ch = RTGetOptPrintError(ch, &ValueUnion);
1800 printUsage(g_pStdErr);
1801 return ch;
1802 }
1803 }
1804
1805 /* Check for mandatory parameters. */
1806 if (!pszFilename)
1807 return errorSyntax("Mandatory --filename option missing\n");
1808
1809 /* just try it */
1810 if (!pszFormat)
1811 {
1812 rc = VDGetFormat(NULL, NULL, pszFilename, VDTYPE_INVALID, &pszBackend, &enmType);
1813 if (RT_FAILURE(rc))
1814 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
1815 pszFormat = pszBackend;
1816 }
1817
1818 rc = VDRepair(pVDIfs, NULL, pszFilename, pszFormat, fDryRun ? VD_REPAIR_DRY_RUN : 0);
1819 if (RT_FAILURE(rc))
1820 rc = errorRuntime("Error while repairing the virtual disk: %Rrf (%Rrc)\n", rc, rc);
1821
1822 if (pszBackend)
1823 RTStrFree(pszBackend);
1824 return rc;
1825}
1826
1827
1828static int handleClearComment(HandlerArg *a)
1829{
1830 int rc = VINF_SUCCESS;
1831 PVDISK pDisk = NULL;
1832 const char *pszFilename = NULL;
1833
1834 /* Parse the command line. */
1835 static const RTGETOPTDEF s_aOptions[] =
1836 {
1837 { "--filename", 'f', RTGETOPT_REQ_STRING }
1838 };
1839 int ch;
1840 RTGETOPTUNION ValueUnion;
1841 RTGETOPTSTATE GetState;
1842 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1843 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1844 {
1845 switch (ch)
1846 {
1847 case 'f': // --filename
1848 pszFilename = ValueUnion.psz;
1849 break;
1850
1851 default:
1852 ch = RTGetOptPrintError(ch, &ValueUnion);
1853 printUsage(g_pStdErr);
1854 return ch;
1855 }
1856 }
1857
1858 /* Check for mandatory parameters. */
1859 if (!pszFilename)
1860 return errorSyntax("Mandatory --filename option missing\n");
1861
1862 /* just try it */
1863 char *pszFormat = NULL;
1864 VDTYPE enmType = VDTYPE_INVALID;
1865 rc = VDGetFormat(NULL, NULL, pszFilename, VDTYPE_INVALID, &pszFormat, &enmType);
1866 if (RT_FAILURE(rc))
1867 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
1868
1869 rc = VDCreate(pVDIfs, enmType, &pDisk);
1870 if (RT_FAILURE(rc))
1871 return errorRuntime("Error while creating the virtual disk container: %Rrf (%Rrc)\n", rc, rc);
1872
1873 /* Open the image */
1874 rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_INFO, NULL);
1875 if (RT_FAILURE(rc))
1876 return errorRuntime("Error while opening the image: %Rrf (%Rrc)\n", rc, rc);
1877
1878 VDSetComment(pDisk, 0, NULL);
1879
1880 VDDestroy(pDisk);
1881 return rc;
1882}
1883
1884
1885static int handleCreateFloppy(HandlerArg *a)
1886{
1887 const char *pszFilename = NULL;
1888 uint64_t cbFloppy = 1474560;
1889 uint16_t cbSector = 0;
1890 uint8_t cHeads = 0;
1891 uint8_t cSectorsPerCluster = 0;
1892 uint8_t cSectorsPerTrack = 0;
1893 uint16_t cRootDirEntries = 0;
1894 uint8_t bMedia = 0;
1895
1896 /* Parse the command line. */
1897 static const RTGETOPTDEF s_aOptions[] =
1898 {
1899 { "--sectors-per-cluster", 'c', RTGETOPT_REQ_UINT8 },
1900 { "--filename", 'f', RTGETOPT_REQ_STRING },
1901 { "--heads", 'h', RTGETOPT_REQ_UINT8 },
1902 { "--media-byte", 'm', RTGETOPT_REQ_UINT8 },
1903 { "--root-dir-entries", 'r', RTGETOPT_REQ_UINT16 },
1904 { "--size", 's', RTGETOPT_REQ_UINT64 },
1905 { "--sector-size", 'S', RTGETOPT_REQ_UINT16 },
1906 { "--sectors-per-track", 't', RTGETOPT_REQ_UINT8 },
1907 };
1908 int ch;
1909 RTGETOPTUNION ValueUnion;
1910 RTGETOPTSTATE GetState;
1911 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1912 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1913 {
1914 switch (ch)
1915 {
1916 case 'c': cSectorsPerCluster = ValueUnion.u8; break;
1917 case 'f': pszFilename = ValueUnion.psz; break;
1918 case 'h': cHeads = ValueUnion.u8; break;
1919 case 'm': bMedia = ValueUnion.u8; break;
1920 case 'r': cRootDirEntries = ValueUnion.u16; break;
1921 case 's': cbFloppy = ValueUnion.u64; break;
1922 case 'S': cbSector = ValueUnion.u16; break;
1923 case 't': cSectorsPerTrack = ValueUnion.u8; break;
1924
1925 default:
1926 ch = RTGetOptPrintError(ch, &ValueUnion);
1927 printUsage(g_pStdErr);
1928 return ch;
1929 }
1930 }
1931
1932 /* Check for mandatory parameters. */
1933 if (!pszFilename)
1934 return errorSyntax("Mandatory --filename option missing\n");
1935
1936 /*
1937 * Do the job.
1938 */
1939 uint32_t offError;
1940 RTERRINFOSTATIC ErrInfo;
1941 RTVFSFILE hVfsFile;
1942 int rc = RTVfsChainOpenFile(pszFilename,
1943 RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_ALL
1944 | (0770 << RTFILE_O_CREATE_MODE_SHIFT),
1945 &hVfsFile, &offError, RTErrInfoInitStatic(&ErrInfo));
1946 if (RT_SUCCESS(rc))
1947 {
1948 rc = RTFsFatVolFormat(hVfsFile, 0, cbFloppy, RTFSFATVOL_FMT_F_FULL, cbSector, cSectorsPerCluster, RTFSFATTYPE_INVALID,
1949 cHeads, cSectorsPerTrack, bMedia, 0 /*cHiddenSectors*/, cRootDirEntries,
1950 RTErrInfoInitStatic(&ErrInfo));
1951 RTVfsFileRelease(hVfsFile);
1952 if (RT_SUCCESS(rc))
1953 return RTEXITCODE_SUCCESS;
1954
1955 if (RTErrInfoIsSet(&ErrInfo.Core))
1956 errorRuntime("Error %Rrc formatting floppy '%s': %s", rc, pszFilename, ErrInfo.Core.pszMsg);
1957 else
1958 errorRuntime("Error formatting floppy '%s': %Rrc", pszFilename, rc);
1959 }
1960 else
1961 RTVfsChainMsgError("RTVfsChainOpenFile", pszFilename, rc, offError, &ErrInfo.Core);
1962 return RTEXITCODE_FAILURE;
1963}
1964
1965
1966static int handleCreateIso(HandlerArg *a)
1967{
1968 return RTFsIsoMakerCmd(a->argc + 1, a->argv - 1);
1969}
1970
1971
1972static int handleClearResize(HandlerArg *a)
1973{
1974 int rc = VINF_SUCCESS;
1975 PVDISK pDisk = NULL;
1976 const char *pszFilename = NULL;
1977 uint64_t cbNew = 0;
1978 VDGEOMETRY LCHSGeometry, PCHSGeometry;
1979
1980 memset(&LCHSGeometry, 0, sizeof(LCHSGeometry));
1981 memset(&PCHSGeometry, 0, sizeof(PCHSGeometry));
1982
1983 /* Parse the command line. */
1984 static const RTGETOPTDEF s_aOptions[] =
1985 {
1986 { "--filename", 'f', RTGETOPT_REQ_STRING },
1987 { "--size", 's', RTGETOPT_REQ_UINT64 }
1988 };
1989 int ch;
1990 RTGETOPTUNION ValueUnion;
1991 RTGETOPTSTATE GetState;
1992 RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0 /* fFlags */);
1993 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
1994 {
1995 switch (ch)
1996 {
1997 case 'f': // --filename
1998 pszFilename = ValueUnion.psz;
1999 break;
2000
2001 case 's': // --size
2002 cbNew = ValueUnion.u64;
2003 break;
2004
2005 default:
2006 ch = RTGetOptPrintError(ch, &ValueUnion);
2007 printUsage(g_pStdErr);
2008 return ch;
2009 }
2010 }
2011
2012 /* Check for mandatory parameters. */
2013 if (!pszFilename)
2014 return errorSyntax("Mandatory --filename option missing\n");
2015
2016 if (!cbNew)
2017 return errorSyntax("Mandatory --size option missing or invalid\n");
2018
2019 /* just try it */
2020 char *pszFormat = NULL;
2021 VDTYPE enmType = VDTYPE_INVALID;
2022 rc = VDGetFormat(NULL, NULL, pszFilename, VDTYPE_INVALID, &pszFormat, &enmType);
2023 if (RT_FAILURE(rc))
2024 return errorSyntax("Format autodetect failed: %Rrc\n", rc);
2025
2026 rc = VDCreate(pVDIfs, enmType, &pDisk);
2027 if (RT_FAILURE(rc))
2028 return errorRuntime("Error while creating the virtual disk container: %Rrf (%Rrc)\n", rc, rc);
2029
2030 /* Open the image */
2031 rc = VDOpen(pDisk, pszFormat, pszFilename, VD_OPEN_FLAGS_NORMAL, NULL);
2032 if (RT_FAILURE(rc))
2033 return errorRuntime("Error while opening the image: %Rrf (%Rrc)\n", rc, rc);
2034
2035 rc = VDResize(pDisk, cbNew, &PCHSGeometry, &LCHSGeometry, NULL);
2036 if (RT_FAILURE(rc))
2037 rc = errorRuntime("Error while resizing the virtual disk: %Rrf (%Rrc)\n", rc, rc);
2038
2039 VDDestroy(pDisk);
2040 return rc;
2041}
2042
2043
2044int main(int argc, char *argv[])
2045{
2046 int exitcode = 0;
2047
2048 int rc = RTR3InitExe(argc, &argv, RTR3INIT_FLAGS_STANDALONE_APP);
2049 if (RT_FAILURE(rc))
2050 return RTMsgInitFailure(rc);
2051
2052 g_pszProgName = RTPathFilename(argv[0]);
2053
2054 bool fShowLogo = false;
2055 int iCmd = 1;
2056 int iCmdArg;
2057
2058 /* global options */
2059 for (int i = 1; i < argc || argc <= iCmd; i++)
2060 {
2061 if ( argc <= iCmd
2062 || !strcmp(argv[i], "help")
2063 || !strcmp(argv[i], "-?")
2064 || !strcmp(argv[i], "-h")
2065 || !strcmp(argv[i], "-help")
2066 || !strcmp(argv[i], "--help"))
2067 {
2068 showLogo(g_pStdOut);
2069 printUsage(g_pStdOut);
2070 return 0;
2071 }
2072
2073 if ( !strcmp(argv[i], "-v")
2074 || !strcmp(argv[i], "-version")
2075 || !strcmp(argv[i], "-Version")
2076 || !strcmp(argv[i], "--version"))
2077 {
2078 /* Print version number, and do nothing else. */
2079 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
2080 return 0;
2081 }
2082
2083 if ( !strcmp(argv[i], "--nologo")
2084 || !strcmp(argv[i], "-nologo")
2085 || !strcmp(argv[i], "-q"))
2086 {
2087 /* suppress the logo */
2088 fShowLogo = false;
2089 iCmd++;
2090 }
2091 else
2092 {
2093 break;
2094 }
2095 }
2096
2097 iCmdArg = iCmd + 1;
2098
2099 if (fShowLogo)
2100 showLogo(g_pStdOut);
2101
2102 /* initialize the VD backend with dummy handlers */
2103 VDINTERFACEERROR vdInterfaceError;
2104 vdInterfaceError.pfnError = handleVDError;
2105 vdInterfaceError.pfnMessage = handleVDMessage;
2106
2107 rc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
2108 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
2109
2110 rc = VDInit();
2111 if (RT_FAILURE(rc))
2112 {
2113 errorSyntax("Initializing backends failed! rc=%Rrc\n", rc);
2114 return 1;
2115 }
2116
2117 /*
2118 * All registered command handlers
2119 */
2120 static const struct
2121 {
2122 const char *command;
2123 int (*handler)(HandlerArg *a);
2124 } s_commandHandlers[] =
2125 {
2126 { "setuuid", handleSetUUID },
2127 { "geometry", handleGeometry },
2128 { "convert", handleConvert },
2129 { "info", handleInfo },
2130 { "compact", handleCompact },
2131 { "createcache", handleCreateCache },
2132 { "createbase", handleCreateBase },
2133 { "createfloppy", handleCreateFloppy },
2134 { "createiso", handleCreateIso },
2135 { "repair", handleRepair },
2136 { "clearcomment", handleClearComment },
2137 { "resize", handleClearResize },
2138 { NULL, NULL }
2139 };
2140
2141 HandlerArg handlerArg = { 0, NULL };
2142 int commandIndex;
2143 for (commandIndex = 0; s_commandHandlers[commandIndex].command != NULL; commandIndex++)
2144 {
2145 if (!strcmp(s_commandHandlers[commandIndex].command, argv[iCmd]))
2146 {
2147 handlerArg.argc = argc - iCmdArg;
2148 handlerArg.argv = &argv[iCmdArg];
2149
2150 exitcode = s_commandHandlers[commandIndex].handler(&handlerArg);
2151 break;
2152 }
2153 }
2154 if (!s_commandHandlers[commandIndex].command)
2155 {
2156 errorSyntax("Invalid command '%s'", argv[iCmd]);
2157 return 1;
2158 }
2159
2160 rc = VDShutdown();
2161 if (RT_FAILURE(rc))
2162 {
2163 errorSyntax("Unloading backends failed! rc=%Rrc\n", rc);
2164 return 1;
2165 }
2166
2167 return exitcode;
2168}
2169
2170/* dummy stub for RuntimeR3 */
2171#ifndef RT_OS_WINDOWS
2172RTDECL(bool) RTAssertShouldPanic(void)
2173{
2174 return true;
2175}
2176#endif
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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