VirtualBox

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

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

Storage: Added a desired format parameter to VDGetFormat() so Main can pass along the device type. Bumped the RAW backend down after the CUE and VISO to prevent (impossible) mixups of tiny files. Extended rawProbe() to look for a valid ISO-9660/UDF descriptor sequence on the image if the caller doesn't say it should be a floppy or hdd, only if that fail go by the extension. Note! the pfnProbe callback should probably get a score return parameter to indicate how confient the backend is about the probe result, as this would prevent the RAW backend from accidentally grabbing images which belongs to a plug-in. bugref:9151

  • 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 79965 2019-07-24 20:32:32Z vboxsync $ */
2/** @file
3 * Standalone image manipulation tool
4 */
5
6/*
7 * Copyright (C) 2010-2019 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