VirtualBox

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

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

iprt/dvm: Some API adjusting to VFS - work in progress. [build fix]

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