VirtualBox

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

Last change on this file was 103522, checked in by vboxsync, 2 months ago

Storage/testcase: Some unused variable fixes, bugref:3409

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

© 2023 Oracle
ContactPrivacy policyTerms of Use