VirtualBox

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

Last change on this file since 82968 was 82968, checked in by vboxsync, 5 years ago

Copyright year updates by scm.

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

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