VirtualBox

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

Last change on this file since 103005 was 103005, checked in by vboxsync, 3 months ago

iprt/asm.h,*: Split out the ASMMem* and related stuff into a separate header, asm-mem.h, so that we can get the RT_ASM_PAGE_SIZE stuff out of the way.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use