VirtualBox

source: vbox/trunk/src/VBox/Frontends/VBoxManage/VBoxManageDisk.cpp@ 72949

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

VBoxManage,manual: Added a new command called 'mediumio' to VBoxManage for making use of the IMediumIO interface.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 84.7 KB
Line 
1/* $Id: VBoxManageDisk.cpp 72949 2018-07-07 16:22:45Z vboxsync $ */
2/** @file
3 * VBoxManage - The disk/medium related commands.
4 */
5
6/*
7 * Copyright (C) 2006-2017 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#ifndef VBOX_ONLY_DOCS
19
20
21/*********************************************************************************************************************************
22* Header Files *
23*********************************************************************************************************************************/
24#include <VBox/com/com.h>
25#include <VBox/com/array.h>
26#include <VBox/com/ErrorInfo.h>
27#include <VBox/com/errorprint.h>
28#include <VBox/com/VirtualBox.h>
29
30#include <iprt/asm.h>
31#include <iprt/file.h>
32#include <iprt/path.h>
33#include <iprt/param.h>
34#include <iprt/stream.h>
35#include <iprt/string.h>
36#include <iprt/ctype.h>
37#include <iprt/getopt.h>
38#include <VBox/log.h>
39#include <VBox/vd.h>
40
41#include "VBoxManage.h"
42using namespace com;
43
44/** Medium category. */
45typedef enum MEDIUMCATEGORY
46{
47 MEDIUMCATEGORY_NONE = 0,
48 MEDIUMCATEGORY_DISK,
49 MEDIUMCATEGORY_DVD,
50 MEDIUMCATEGORY_FLOPPY
51} MEDIUMCATEGORY;
52
53
54
55// funcs
56///////////////////////////////////////////////////////////////////////////////
57
58
59static DECLCALLBACK(void) handleVDError(void *pvUser, int rc, RT_SRC_POS_DECL, const char *pszFormat, va_list va)
60{
61 RT_NOREF(pvUser);
62 RTMsgErrorV(pszFormat, va);
63 RTMsgError("Error code %Rrc at %s(%u) in function %s", rc, RT_SRC_POS_ARGS);
64}
65
66static int parseMediumVariant(const char *psz, MediumVariant_T *pMediumVariant)
67{
68 int rc = VINF_SUCCESS;
69 unsigned uMediumVariant = (unsigned)(*pMediumVariant);
70 while (psz && *psz && RT_SUCCESS(rc))
71 {
72 size_t len;
73 const char *pszComma = strchr(psz, ',');
74 if (pszComma)
75 len = pszComma - psz;
76 else
77 len = strlen(psz);
78 if (len > 0)
79 {
80 // Parsing is intentionally inconsistent: "standard" resets the
81 // variant, whereas the other flags are cumulative.
82 if (!RTStrNICmp(psz, "standard", len))
83 uMediumVariant = MediumVariant_Standard;
84 else if ( !RTStrNICmp(psz, "fixed", len)
85 || !RTStrNICmp(psz, "static", len))
86 uMediumVariant |= MediumVariant_Fixed;
87 else if (!RTStrNICmp(psz, "Diff", len))
88 uMediumVariant |= MediumVariant_Diff;
89 else if (!RTStrNICmp(psz, "split2g", len))
90 uMediumVariant |= MediumVariant_VmdkSplit2G;
91 else if ( !RTStrNICmp(psz, "stream", len)
92 || !RTStrNICmp(psz, "streamoptimized", len))
93 uMediumVariant |= MediumVariant_VmdkStreamOptimized;
94 else if (!RTStrNICmp(psz, "esx", len))
95 uMediumVariant |= MediumVariant_VmdkESX;
96 else if (!RTStrNICmp(psz, "formatted", len))
97 uMediumVariant |= MediumVariant_Formatted;
98 else
99 rc = VERR_PARSE_ERROR;
100 }
101 if (pszComma)
102 psz += len + 1;
103 else
104 psz += len;
105 }
106
107 if (RT_SUCCESS(rc))
108 *pMediumVariant = (MediumVariant_T)uMediumVariant;
109 return rc;
110}
111
112int parseMediumType(const char *psz, MediumType_T *penmMediumType)
113{
114 int rc = VINF_SUCCESS;
115 MediumType_T enmMediumType = MediumType_Normal;
116 if (!RTStrICmp(psz, "normal"))
117 enmMediumType = MediumType_Normal;
118 else if (!RTStrICmp(psz, "immutable"))
119 enmMediumType = MediumType_Immutable;
120 else if (!RTStrICmp(psz, "writethrough"))
121 enmMediumType = MediumType_Writethrough;
122 else if (!RTStrICmp(psz, "shareable"))
123 enmMediumType = MediumType_Shareable;
124 else if (!RTStrICmp(psz, "readonly"))
125 enmMediumType = MediumType_Readonly;
126 else if (!RTStrICmp(psz, "multiattach"))
127 enmMediumType = MediumType_MultiAttach;
128 else
129 rc = VERR_PARSE_ERROR;
130
131 if (RT_SUCCESS(rc))
132 *penmMediumType = enmMediumType;
133 return rc;
134}
135
136/** @todo move this into getopt, as getting bool values is generic */
137int parseBool(const char *psz, bool *pb)
138{
139 int rc = VINF_SUCCESS;
140 if ( !RTStrICmp(psz, "on")
141 || !RTStrICmp(psz, "yes")
142 || !RTStrICmp(psz, "true")
143 || !RTStrICmp(psz, "1")
144 || !RTStrICmp(psz, "enable")
145 || !RTStrICmp(psz, "enabled"))
146 {
147 *pb = true;
148 }
149 else if ( !RTStrICmp(psz, "off")
150 || !RTStrICmp(psz, "no")
151 || !RTStrICmp(psz, "false")
152 || !RTStrICmp(psz, "0")
153 || !RTStrICmp(psz, "disable")
154 || !RTStrICmp(psz, "disabled"))
155 {
156 *pb = false;
157 }
158 else
159 rc = VERR_PARSE_ERROR;
160
161 return rc;
162}
163
164HRESULT openMedium(HandlerArg *a, const char *pszFilenameOrUuid,
165 DeviceType_T enmDevType, AccessMode_T enmAccessMode,
166 ComPtr<IMedium> &pMedium, bool fForceNewUuidOnOpen,
167 bool fSilent)
168{
169 HRESULT rc;
170 Guid id(pszFilenameOrUuid);
171 char szFilenameAbs[RTPATH_MAX] = "";
172
173 /* If it is no UUID, convert the filename to an absolute one. */
174 if (!id.isValid())
175 {
176 int irc = RTPathAbs(pszFilenameOrUuid, szFilenameAbs, sizeof(szFilenameAbs));
177 if (RT_FAILURE(irc))
178 {
179 if (!fSilent)
180 RTMsgError("Cannot convert filename \"%s\" to absolute path", pszFilenameOrUuid);
181 return E_FAIL;
182 }
183 pszFilenameOrUuid = szFilenameAbs;
184 }
185
186 if (!fSilent)
187 CHECK_ERROR(a->virtualBox, OpenMedium(Bstr(pszFilenameOrUuid).raw(),
188 enmDevType,
189 enmAccessMode,
190 fForceNewUuidOnOpen,
191 pMedium.asOutParam()));
192 else
193 rc = a->virtualBox->OpenMedium(Bstr(pszFilenameOrUuid).raw(),
194 enmDevType,
195 enmAccessMode,
196 fForceNewUuidOnOpen,
197 pMedium.asOutParam());
198
199 return rc;
200}
201
202static HRESULT createMedium(HandlerArg *a, const char *pszFormat,
203 const char *pszFilename, DeviceType_T enmDevType,
204 AccessMode_T enmAccessMode, ComPtr<IMedium> &pMedium)
205{
206 HRESULT rc;
207 char szFilenameAbs[RTPATH_MAX] = "";
208
209 /** @todo laziness shortcut. should really check the MediumFormatCapabilities */
210 if (RTStrICmp(pszFormat, "iSCSI"))
211 {
212 int irc = RTPathAbs(pszFilename, szFilenameAbs, sizeof(szFilenameAbs));
213 if (RT_FAILURE(irc))
214 {
215 RTMsgError("Cannot convert filename \"%s\" to absolute path", pszFilename);
216 return E_FAIL;
217 }
218 pszFilename = szFilenameAbs;
219 }
220
221 CHECK_ERROR(a->virtualBox, CreateMedium(Bstr(pszFormat).raw(),
222 Bstr(pszFilename).raw(),
223 enmAccessMode,
224 enmDevType,
225 pMedium.asOutParam()));
226 return rc;
227}
228
229static const RTGETOPTDEF g_aCreateMediumOptions[] =
230{
231 { "disk", 'H', RTGETOPT_REQ_NOTHING },
232 { "dvd", 'D', RTGETOPT_REQ_NOTHING },
233 { "floppy", 'L', RTGETOPT_REQ_NOTHING },
234 { "--filename", 'f', RTGETOPT_REQ_STRING },
235 { "-filename", 'f', RTGETOPT_REQ_STRING }, // deprecated
236 { "--diffparent", 'd', RTGETOPT_REQ_STRING },
237 { "--size", 's', RTGETOPT_REQ_UINT64 },
238 { "-size", 's', RTGETOPT_REQ_UINT64 }, // deprecated
239 { "--sizebyte", 'S', RTGETOPT_REQ_UINT64 },
240 { "--format", 'o', RTGETOPT_REQ_STRING },
241 { "-format", 'o', RTGETOPT_REQ_STRING }, // deprecated
242 { "--static", 'F', RTGETOPT_REQ_NOTHING },
243 { "-static", 'F', RTGETOPT_REQ_NOTHING }, // deprecated
244 { "--variant", 'm', RTGETOPT_REQ_STRING },
245 { "-variant", 'm', RTGETOPT_REQ_STRING }, // deprecated
246};
247
248RTEXITCODE handleCreateMedium(HandlerArg *a)
249{
250 HRESULT rc;
251 int vrc;
252 const char *filename = NULL;
253 const char *diffparent = NULL;
254 uint64_t size = 0;
255 enum {
256 CMD_NONE,
257 CMD_DISK,
258 CMD_DVD,
259 CMD_FLOPPY
260 } cmd = CMD_NONE;
261 const char *format = NULL;
262 bool fBase = true;
263 MediumVariant_T enmMediumVariant = MediumVariant_Standard;
264
265 int c;
266 RTGETOPTUNION ValueUnion;
267 RTGETOPTSTATE GetState;
268 // start at 0 because main() has hacked both the argc and argv given to us
269 RTGetOptInit(&GetState, a->argc, a->argv, g_aCreateMediumOptions, RT_ELEMENTS(g_aCreateMediumOptions),
270 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
271 while ((c = RTGetOpt(&GetState, &ValueUnion)))
272 {
273 switch (c)
274 {
275 case 'H': // disk
276 if (cmd != CMD_NONE)
277 return errorSyntax(USAGE_CREATEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
278 cmd = CMD_DISK;
279 break;
280
281 case 'D': // DVD
282 if (cmd != CMD_NONE)
283 return errorSyntax(USAGE_CREATEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
284 cmd = CMD_DVD;
285 break;
286
287 case 'L': // floppy
288 if (cmd != CMD_NONE)
289 return errorSyntax(USAGE_CREATEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
290 cmd = CMD_FLOPPY;
291 break;
292
293 case 'f': // --filename
294 filename = ValueUnion.psz;
295 break;
296
297 case 'd': // --diffparent
298 diffparent = ValueUnion.psz;
299 fBase = false;
300 break;
301
302 case 's': // --size
303 size = ValueUnion.u64 * _1M;
304 break;
305
306 case 'S': // --sizebyte
307 size = ValueUnion.u64;
308 break;
309
310 case 'o': // --format
311 format = ValueUnion.psz;
312 break;
313
314 case 'F': // --static ("fixed"/"flat")
315 {
316 unsigned uMediumVariant = (unsigned)enmMediumVariant;
317 uMediumVariant |= MediumVariant_Fixed;
318 enmMediumVariant = (MediumVariant_T)uMediumVariant;
319 break;
320 }
321
322 case 'm': // --variant
323 vrc = parseMediumVariant(ValueUnion.psz, &enmMediumVariant);
324 if (RT_FAILURE(vrc))
325 return errorArgument("Invalid medium variant '%s'", ValueUnion.psz);
326 break;
327
328 case VINF_GETOPT_NOT_OPTION:
329 return errorSyntax(USAGE_CREATEMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
330
331 default:
332 if (c > 0)
333 {
334 if (RT_C_IS_PRINT(c))
335 return errorSyntax(USAGE_CREATEMEDIUM, "Invalid option -%c", c);
336 else
337 return errorSyntax(USAGE_CREATEMEDIUM, "Invalid option case %i", c);
338 }
339 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
340 return errorSyntax(USAGE_CREATEMEDIUM, "unknown option: %s\n", ValueUnion.psz);
341 else if (ValueUnion.pDef)
342 return errorSyntax(USAGE_CREATEMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
343 else
344 return errorSyntax(USAGE_CREATEMEDIUM, "error: %Rrs", c);
345 }
346 }
347
348 /* check the outcome */
349 if (cmd == CMD_NONE)
350 cmd = CMD_DISK;
351 ComPtr<IMedium> pParentMedium;
352 if (fBase)
353 {
354 if ( !filename
355 || !*filename
356 || size == 0)
357 return errorSyntax(USAGE_CREATEMEDIUM, "Parameters --filename and --size are required");
358 if (!format || !*format)
359 {
360 if (cmd == CMD_DISK)
361 format = "VDI";
362 else if (cmd == CMD_DVD || cmd == CMD_FLOPPY)
363 {
364 format = "RAW";
365 unsigned uMediumVariant = (unsigned)enmMediumVariant;
366 uMediumVariant |= MediumVariant_Fixed;
367 enmMediumVariant = (MediumVariant_T)uMediumVariant;
368 }
369 }
370 }
371 else
372 {
373 if ( !filename
374 || !*filename)
375 return errorSyntax(USAGE_CREATEMEDIUM, "Parameters --filename is required");
376 size = 0;
377 if (cmd != CMD_DISK)
378 return errorSyntax(USAGE_CREATEMEDIUM, "Creating a differencing medium is only supported for hard disks");
379 enmMediumVariant = MediumVariant_Diff;
380 if (!format || !*format)
381 {
382 const char *pszExt = RTPathSuffix(filename);
383 /* Skip over . if there is an extension. */
384 if (pszExt)
385 pszExt++;
386 if (!pszExt || !*pszExt)
387 format = "VDI";
388 else
389 format = pszExt;
390 }
391 rc = openMedium(a, diffparent, DeviceType_HardDisk,
392 AccessMode_ReadWrite, pParentMedium,
393 false /* fForceNewUuidOnOpen */, false /* fSilent */);
394 if (FAILED(rc))
395 return RTEXITCODE_FAILURE;
396 if (pParentMedium.isNull())
397 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid parent hard disk reference, avoiding crash");
398 MediumState_T state;
399 CHECK_ERROR(pParentMedium, COMGETTER(State)(&state));
400 if (FAILED(rc))
401 return RTEXITCODE_FAILURE;
402 if (state == MediumState_Inaccessible)
403 {
404 CHECK_ERROR(pParentMedium, RefreshState(&state));
405 if (FAILED(rc))
406 return RTEXITCODE_FAILURE;
407 }
408 }
409 /* check for filename extension */
410 /** @todo use IMediumFormat to cover all extensions generically */
411 Utf8Str strName(filename);
412 if (!RTPathHasSuffix(strName.c_str()))
413 {
414 Utf8Str strFormat(format);
415 if (cmd == CMD_DISK)
416 {
417 if (strFormat.compare("vmdk", RTCString::CaseInsensitive) == 0)
418 strName.append(".vmdk");
419 else if (strFormat.compare("vhd", RTCString::CaseInsensitive) == 0)
420 strName.append(".vhd");
421 else
422 strName.append(".vdi");
423 } else if (cmd == CMD_DVD)
424 strName.append(".iso");
425 else if (cmd == CMD_FLOPPY)
426 strName.append(".img");
427 filename = strName.c_str();
428 }
429
430 ComPtr<IMedium> pMedium;
431 if (cmd == CMD_DISK)
432 rc = createMedium(a, format, filename, DeviceType_HardDisk,
433 AccessMode_ReadWrite, pMedium);
434 else if (cmd == CMD_DVD)
435 rc = createMedium(a, format, filename, DeviceType_DVD,
436 AccessMode_ReadOnly, pMedium);
437 else if (cmd == CMD_FLOPPY)
438 rc = createMedium(a, format, filename, DeviceType_Floppy,
439 AccessMode_ReadWrite, pMedium);
440 else
441 rc = E_INVALIDARG; /* cannot happen but make gcc happy */
442
443 if (SUCCEEDED(rc) && pMedium)
444 {
445 ComPtr<IProgress> pProgress;
446 com::SafeArray<MediumVariant_T> l_variants(sizeof(MediumVariant_T)*8);
447
448 for (ULONG i = 0; i < l_variants.size(); ++i)
449 {
450 ULONG temp = enmMediumVariant;
451 temp &= 1<<i;
452 l_variants [i] = (MediumVariant_T)temp;
453 }
454
455 if (fBase)
456 CHECK_ERROR(pMedium, CreateBaseStorage(size, ComSafeArrayAsInParam(l_variants), pProgress.asOutParam()));
457 else
458 CHECK_ERROR(pParentMedium, CreateDiffStorage(pMedium, ComSafeArrayAsInParam(l_variants), pProgress.asOutParam()));
459 if (SUCCEEDED(rc) && pProgress)
460 {
461 rc = showProgress(pProgress);
462 CHECK_PROGRESS_ERROR(pProgress, ("Failed to create medium"));
463 }
464 }
465
466 if (SUCCEEDED(rc) && pMedium)
467 {
468 Bstr uuid;
469 CHECK_ERROR(pMedium, COMGETTER(Id)(uuid.asOutParam()));
470 RTPrintf("Medium created. UUID: %s\n", Utf8Str(uuid).c_str());
471
472 //CHECK_ERROR(pMedium, Close());
473 }
474 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
475}
476
477static const RTGETOPTDEF g_aModifyMediumOptions[] =
478{
479 { "disk", 'H', RTGETOPT_REQ_NOTHING },
480 { "dvd", 'D', RTGETOPT_REQ_NOTHING },
481 { "floppy", 'L', RTGETOPT_REQ_NOTHING },
482 { "--type", 't', RTGETOPT_REQ_STRING },
483 { "-type", 't', RTGETOPT_REQ_STRING }, // deprecated
484 { "settype", 't', RTGETOPT_REQ_STRING }, // deprecated
485 { "--autoreset", 'z', RTGETOPT_REQ_STRING },
486 { "-autoreset", 'z', RTGETOPT_REQ_STRING }, // deprecated
487 { "autoreset", 'z', RTGETOPT_REQ_STRING }, // deprecated
488 { "--property", 'p', RTGETOPT_REQ_STRING },
489 { "--compact", 'c', RTGETOPT_REQ_NOTHING },
490 { "-compact", 'c', RTGETOPT_REQ_NOTHING }, // deprecated
491 { "compact", 'c', RTGETOPT_REQ_NOTHING }, // deprecated
492 { "--resize", 'r', RTGETOPT_REQ_UINT64 },
493 { "--resizebyte", 'R', RTGETOPT_REQ_UINT64 },
494 { "--move", 'm', RTGETOPT_REQ_STRING },
495 { "--description", 'd', RTGETOPT_REQ_STRING }
496};
497
498RTEXITCODE handleModifyMedium(HandlerArg *a)
499{
500 HRESULT rc;
501 int vrc;
502 enum {
503 CMD_NONE,
504 CMD_DISK,
505 CMD_DVD,
506 CMD_FLOPPY
507 } cmd = CMD_NONE;
508 ComPtr<IMedium> pMedium;
509 MediumType_T enmMediumType = MediumType_Normal; /* Shut up MSC */
510 bool AutoReset = false;
511 SafeArray<BSTR> mediumPropNames;
512 SafeArray<BSTR> mediumPropValues;
513 bool fModifyMediumType = false;
514 bool fModifyAutoReset = false;
515 bool fModifyProperties = false;
516 bool fModifyCompact = false;
517 bool fModifyResize = false;
518 bool fModifyResizeMB = false;
519 bool fModifyLocation = false;
520 bool fModifyDescription = false;
521 uint64_t cbResize = 0;
522 const char *pszFilenameOrUuid = NULL;
523 char *pszNewLocation = NULL;
524
525 int c;
526 RTGETOPTUNION ValueUnion;
527 RTGETOPTSTATE GetState;
528 // start at 0 because main() has hacked both the argc and argv given to us
529 RTGetOptInit(&GetState, a->argc, a->argv, g_aModifyMediumOptions, RT_ELEMENTS(g_aModifyMediumOptions),
530 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
531 while ((c = RTGetOpt(&GetState, &ValueUnion)))
532 {
533 switch (c)
534 {
535 case 'H': // disk
536 if (cmd != CMD_NONE)
537 return errorSyntax(USAGE_MODIFYMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
538 cmd = CMD_DISK;
539 break;
540
541 case 'D': // DVD
542 if (cmd != CMD_NONE)
543 return errorSyntax(USAGE_MODIFYMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
544 cmd = CMD_DVD;
545 break;
546
547 case 'L': // floppy
548 if (cmd != CMD_NONE)
549 return errorSyntax(USAGE_MODIFYMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
550 cmd = CMD_FLOPPY;
551 break;
552
553 case 't': // --type
554 vrc = parseMediumType(ValueUnion.psz, &enmMediumType);
555 if (RT_FAILURE(vrc))
556 return errorArgument("Invalid medium type '%s'", ValueUnion.psz);
557 fModifyMediumType = true;
558 break;
559
560 case 'z': // --autoreset
561 vrc = parseBool(ValueUnion.psz, &AutoReset);
562 if (RT_FAILURE(vrc))
563 return errorArgument("Invalid autoreset parameter '%s'", ValueUnion.psz);
564 fModifyAutoReset = true;
565 break;
566
567 case 'p': // --property
568 {
569 /* Parse 'name=value' */
570 char *pszProperty = RTStrDup(ValueUnion.psz);
571 if (pszProperty)
572 {
573 char *pDelimiter = strchr(pszProperty, '=');
574 if (pDelimiter)
575 {
576 *pDelimiter = '\0';
577
578 Bstr bstrName(pszProperty);
579 Bstr bstrValue(&pDelimiter[1]);
580 bstrName.detachTo(mediumPropNames.appendedRaw());
581 bstrValue.detachTo(mediumPropValues.appendedRaw());
582 fModifyProperties = true;
583 }
584 else
585 {
586 errorArgument("Invalid --property argument '%s'", ValueUnion.psz);
587 rc = E_FAIL;
588 }
589 RTStrFree(pszProperty);
590 }
591 else
592 {
593 RTStrmPrintf(g_pStdErr, "Error: Failed to allocate memory for medium property '%s'\n", ValueUnion.psz);
594 rc = E_FAIL;
595 }
596 break;
597 }
598
599 case 'c': // --compact
600 fModifyCompact = true;
601 break;
602
603 case 'r': // --resize
604 cbResize = ValueUnion.u64 * _1M;
605 fModifyResize = true;
606 fModifyResizeMB = true; // do sanity check!
607 break;
608
609 case 'R': // --resizebyte
610 cbResize = ValueUnion.u64;
611 fModifyResize = true;
612 break;
613
614 case 'm': // --move
615 /* Get a new location */
616 pszNewLocation = RTPathAbsDup(ValueUnion.psz);
617 fModifyLocation = true;
618 break;
619
620 case 'd': // --description
621 /* Get a new description */
622 pszNewLocation = RTStrDup(ValueUnion.psz);
623 fModifyDescription = true;
624 break;
625
626 case VINF_GETOPT_NOT_OPTION:
627 if (!pszFilenameOrUuid)
628 pszFilenameOrUuid = ValueUnion.psz;
629 else
630 return errorSyntax(USAGE_MODIFYMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
631 break;
632
633 default:
634 if (c > 0)
635 {
636 if (RT_C_IS_PRINT(c))
637 return errorSyntax(USAGE_MODIFYMEDIUM, "Invalid option -%c", c);
638 else
639 return errorSyntax(USAGE_MODIFYMEDIUM, "Invalid option case %i", c);
640 }
641 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
642 return errorSyntax(USAGE_MODIFYMEDIUM, "unknown option: %s\n", ValueUnion.psz);
643 else if (ValueUnion.pDef)
644 return errorSyntax(USAGE_MODIFYMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
645 else
646 return errorSyntax(USAGE_MODIFYMEDIUM, "error: %Rrs", c);
647 }
648 }
649
650 if (cmd == CMD_NONE)
651 cmd = CMD_DISK;
652
653 if (!pszFilenameOrUuid)
654 return errorSyntax(USAGE_MODIFYMEDIUM, "Medium name or UUID required");
655
656 if (!fModifyMediumType
657 && !fModifyAutoReset
658 && !fModifyProperties
659 && !fModifyCompact
660 && !fModifyResize
661 && !fModifyLocation
662 && !fModifyDescription)
663 return errorSyntax(USAGE_MODIFYMEDIUM, "No operation specified");
664
665 /* Always open the medium if necessary, there is no other way. */
666 if (cmd == CMD_DISK)
667 rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
668 AccessMode_ReadWrite, pMedium,
669 false /* fForceNewUuidOnOpen */, false /* fSilent */);
670 else if (cmd == CMD_DVD)
671 rc = openMedium(a, pszFilenameOrUuid, DeviceType_DVD,
672 AccessMode_ReadOnly, pMedium,
673 false /* fForceNewUuidOnOpen */, false /* fSilent */);
674 else if (cmd == CMD_FLOPPY)
675 rc = openMedium(a, pszFilenameOrUuid, DeviceType_Floppy,
676 AccessMode_ReadWrite, pMedium,
677 false /* fForceNewUuidOnOpen */, false /* fSilent */);
678 else
679 rc = E_INVALIDARG; /* cannot happen but make gcc happy */
680 if (FAILED(rc))
681 return RTEXITCODE_FAILURE;
682 if (pMedium.isNull())
683 {
684 RTMsgError("Invalid medium reference, avoiding crash");
685 return RTEXITCODE_FAILURE;
686 }
687
688 if ( fModifyResize
689 && fModifyResizeMB)
690 {
691 // Sanity check
692 //
693 // In general users should know what they do but in this case users have no
694 // alternative to VBoxManage. If happens that one wants to resize the disk
695 // and uses --resize and does not consider that this parameter expects the
696 // new medium size in MB not Byte. If the operation is started and then
697 // aborted by the user, the result is most likely a medium which doesn't
698 // work anymore.
699 MediumState_T state;
700 pMedium->RefreshState(&state);
701 LONG64 logicalSize;
702 pMedium->COMGETTER(LogicalSize)(&logicalSize);
703 if (cbResize > (uint64_t)logicalSize * 1000)
704 {
705 RTMsgError("Error: Attempt to resize the medium from %RU64.%RU64 MB to %RU64.%RU64 MB. Use --resizebyte if this is intended!\n",
706 logicalSize / _1M, (logicalSize % _1M) / (_1M / 10), cbResize / _1M, (cbResize % _1M) / (_1M / 10));
707 return RTEXITCODE_FAILURE;
708 }
709 }
710
711 if (fModifyMediumType)
712 {
713 MediumType_T enmCurrMediumType;
714 CHECK_ERROR(pMedium, COMGETTER(Type)(&enmCurrMediumType));
715
716 if (enmCurrMediumType != enmMediumType)
717 CHECK_ERROR(pMedium, COMSETTER(Type)(enmMediumType));
718 }
719
720 if (fModifyAutoReset)
721 {
722 CHECK_ERROR(pMedium, COMSETTER(AutoReset)(AutoReset));
723 }
724
725 if (fModifyProperties)
726 {
727 CHECK_ERROR(pMedium, SetProperties(ComSafeArrayAsInParam(mediumPropNames), ComSafeArrayAsInParam(mediumPropValues)));
728 }
729
730 if (fModifyCompact)
731 {
732 ComPtr<IProgress> pProgress;
733 CHECK_ERROR(pMedium, Compact(pProgress.asOutParam()));
734 if (SUCCEEDED(rc))
735 rc = showProgress(pProgress);
736 if (FAILED(rc))
737 {
738 if (rc == E_NOTIMPL)
739 RTMsgError("Compact medium operation is not implemented!");
740 else if (rc == VBOX_E_NOT_SUPPORTED)
741 RTMsgError("Compact medium operation for this format is not implemented yet!");
742 else if (!pProgress.isNull())
743 CHECK_PROGRESS_ERROR(pProgress, ("Failed to compact medium"));
744 else
745 RTMsgError("Failed to compact medium!");
746 }
747 }
748
749 if (fModifyResize)
750 {
751 ComPtr<IProgress> pProgress;
752 CHECK_ERROR(pMedium, Resize(cbResize, pProgress.asOutParam()));
753 if (SUCCEEDED(rc))
754 rc = showProgress(pProgress);
755 if (FAILED(rc))
756 {
757 if (rc == E_NOTIMPL)
758 RTMsgError("Resize medium operation is not implemented!");
759 else if (rc == VBOX_E_NOT_SUPPORTED)
760 RTMsgError("Resize medium operation for this format is not implemented yet!");
761 else if (!pProgress.isNull())
762 CHECK_PROGRESS_ERROR(pProgress, ("Failed to resize medium"));
763 else
764 RTMsgError("Failed to resize medium!");
765 }
766 }
767
768 if (fModifyLocation)
769 {
770 do
771 {
772 ComPtr<IProgress> pProgress;
773 Utf8Str strLocation(pszNewLocation);
774 RTStrFree(pszNewLocation);
775 CHECK_ERROR(pMedium, SetLocation(Bstr(strLocation).raw(), pProgress.asOutParam()));
776
777 if (SUCCEEDED(rc) && !pProgress.isNull())
778 {
779 rc = showProgress(pProgress);
780 CHECK_PROGRESS_ERROR(pProgress, ("Failed to move medium"));
781 }
782
783 Bstr uuid;
784 CHECK_ERROR_BREAK(pMedium, COMGETTER(Id)(uuid.asOutParam()));
785
786 RTPrintf("Move medium with UUID %s finished \n", Utf8Str(uuid).c_str());
787 }
788 while (0);
789 }
790
791 if (fModifyDescription)
792 {
793 CHECK_ERROR(pMedium, COMSETTER(Description)(Bstr(pszNewLocation).raw()));
794
795 RTPrintf("Medium description has been changed. \n");
796 }
797
798 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
799}
800
801static const RTGETOPTDEF g_aCloneMediumOptions[] =
802{
803 { "disk", 'd', RTGETOPT_REQ_NOTHING },
804 { "dvd", 'D', RTGETOPT_REQ_NOTHING },
805 { "floppy", 'f', RTGETOPT_REQ_NOTHING },
806 { "--format", 'o', RTGETOPT_REQ_STRING },
807 { "-format", 'o', RTGETOPT_REQ_STRING },
808 { "--static", 'F', RTGETOPT_REQ_NOTHING },
809 { "-static", 'F', RTGETOPT_REQ_NOTHING },
810 { "--existing", 'E', RTGETOPT_REQ_NOTHING },
811 { "--variant", 'm', RTGETOPT_REQ_STRING },
812 { "-variant", 'm', RTGETOPT_REQ_STRING },
813};
814
815RTEXITCODE handleCloneMedium(HandlerArg *a)
816{
817 HRESULT rc;
818 int vrc;
819 enum {
820 CMD_NONE,
821 CMD_DISK,
822 CMD_DVD,
823 CMD_FLOPPY
824 } cmd = CMD_NONE;
825 const char *pszSrc = NULL;
826 const char *pszDst = NULL;
827 Bstr format;
828 MediumVariant_T enmMediumVariant = MediumVariant_Standard;
829 bool fExisting = false;
830
831 int c;
832 RTGETOPTUNION ValueUnion;
833 RTGETOPTSTATE GetState;
834 // start at 0 because main() has hacked both the argc and argv given to us
835 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloneMediumOptions, RT_ELEMENTS(g_aCloneMediumOptions),
836 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
837 while ((c = RTGetOpt(&GetState, &ValueUnion)))
838 {
839 switch (c)
840 {
841 case 'd': // disk
842 if (cmd != CMD_NONE)
843 return errorSyntax(USAGE_CLONEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
844 cmd = CMD_DISK;
845 break;
846
847 case 'D': // DVD
848 if (cmd != CMD_NONE)
849 return errorSyntax(USAGE_CLONEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
850 cmd = CMD_DVD;
851 break;
852
853 case 'f': // floppy
854 if (cmd != CMD_NONE)
855 return errorSyntax(USAGE_CLONEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
856 cmd = CMD_FLOPPY;
857 break;
858
859 case 'o': // --format
860 format = ValueUnion.psz;
861 break;
862
863 case 'F': // --static
864 {
865 unsigned uMediumVariant = (unsigned)enmMediumVariant;
866 uMediumVariant |= MediumVariant_Fixed;
867 enmMediumVariant = (MediumVariant_T)uMediumVariant;
868 break;
869 }
870
871 case 'E': // --existing
872 fExisting = true;
873 break;
874
875 case 'm': // --variant
876 vrc = parseMediumVariant(ValueUnion.psz, &enmMediumVariant);
877 if (RT_FAILURE(vrc))
878 return errorArgument("Invalid medium variant '%s'", ValueUnion.psz);
879 break;
880
881 case VINF_GETOPT_NOT_OPTION:
882 if (!pszSrc)
883 pszSrc = ValueUnion.psz;
884 else if (!pszDst)
885 pszDst = ValueUnion.psz;
886 else
887 return errorSyntax(USAGE_CLONEMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
888 break;
889
890 default:
891 if (c > 0)
892 {
893 if (RT_C_IS_GRAPH(c))
894 return errorSyntax(USAGE_CLONEMEDIUM, "unhandled option: -%c", c);
895 else
896 return errorSyntax(USAGE_CLONEMEDIUM, "unhandled option: %i", c);
897 }
898 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
899 return errorSyntax(USAGE_CLONEMEDIUM, "unknown option: %s", ValueUnion.psz);
900 else if (ValueUnion.pDef)
901 return errorSyntax(USAGE_CLONEMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
902 else
903 return errorSyntax(USAGE_CLONEMEDIUM, "error: %Rrs", c);
904 }
905 }
906
907 if (cmd == CMD_NONE)
908 cmd = CMD_DISK;
909 if (!pszSrc)
910 return errorSyntax(USAGE_CLONEMEDIUM, "Mandatory UUID or input file parameter missing");
911 if (!pszDst)
912 return errorSyntax(USAGE_CLONEMEDIUM, "Mandatory output file parameter missing");
913 if (fExisting && (!format.isEmpty() || enmMediumVariant != MediumType_Normal))
914 return errorSyntax(USAGE_CLONEMEDIUM, "Specified options which cannot be used with --existing");
915
916 ComPtr<IMedium> pSrcMedium;
917 ComPtr<IMedium> pDstMedium;
918
919 if (cmd == CMD_DISK)
920 rc = openMedium(a, pszSrc, DeviceType_HardDisk, AccessMode_ReadOnly, pSrcMedium,
921 false /* fForceNewUuidOnOpen */, false /* fSilent */);
922 else if (cmd == CMD_DVD)
923 rc = openMedium(a, pszSrc, DeviceType_DVD, AccessMode_ReadOnly, pSrcMedium,
924 false /* fForceNewUuidOnOpen */, false /* fSilent */);
925 else if (cmd == CMD_FLOPPY)
926 rc = openMedium(a, pszSrc, DeviceType_Floppy, AccessMode_ReadOnly, pSrcMedium,
927 false /* fForceNewUuidOnOpen */, false /* fSilent */);
928 else
929 rc = E_INVALIDARG; /* cannot happen but make gcc happy */
930 if (FAILED(rc))
931 return RTEXITCODE_FAILURE;
932
933 do
934 {
935 /* open/create destination medium */
936 if (fExisting)
937 {
938 if (cmd == CMD_DISK)
939 rc = openMedium(a, pszDst, DeviceType_HardDisk, AccessMode_ReadWrite, pDstMedium,
940 false /* fForceNewUuidOnOpen */, false /* fSilent */);
941 else if (cmd == CMD_DVD)
942 rc = openMedium(a, pszDst, DeviceType_DVD, AccessMode_ReadOnly, pDstMedium,
943 false /* fForceNewUuidOnOpen */, false /* fSilent */);
944 else if (cmd == CMD_FLOPPY)
945 rc = openMedium(a, pszDst, DeviceType_Floppy, AccessMode_ReadWrite, pDstMedium,
946 false /* fForceNewUuidOnOpen */, false /* fSilent */);
947 if (FAILED(rc))
948 break;
949
950 /* Perform accessibility check now. */
951 MediumState_T state;
952 CHECK_ERROR_BREAK(pDstMedium, RefreshState(&state));
953 CHECK_ERROR_BREAK(pDstMedium, COMGETTER(Format)(format.asOutParam()));
954 }
955 else
956 {
957 /*
958 * In case the format is unspecified check that the source medium supports
959 * image creation and use the same format for the destination image.
960 * Use the default image format if it is not supported.
961 */
962 if (format.isEmpty())
963 {
964 ComPtr<IMediumFormat> pMediumFmt;
965 com::SafeArray<MediumFormatCapabilities_T> l_caps;
966 CHECK_ERROR_BREAK(pSrcMedium, COMGETTER(MediumFormat)(pMediumFmt.asOutParam()));
967 CHECK_ERROR_BREAK(pMediumFmt, COMGETTER(Capabilities)(ComSafeArrayAsOutParam(l_caps)));
968 ULONG caps=0;
969 for (size_t i = 0; i < l_caps.size(); i++)
970 caps |= l_caps[i];
971 if (caps & ( MediumFormatCapabilities_CreateDynamic
972 | MediumFormatCapabilities_CreateFixed))
973 CHECK_ERROR_BREAK(pMediumFmt, COMGETTER(Id)(format.asOutParam()));
974 }
975 Utf8Str strFormat(format);
976 if (cmd == CMD_DISK)
977 rc = createMedium(a, strFormat.c_str(), pszDst, DeviceType_HardDisk,
978 AccessMode_ReadWrite, pDstMedium);
979 else if (cmd == CMD_DVD)
980 rc = createMedium(a, strFormat.c_str(), pszDst, DeviceType_DVD,
981 AccessMode_ReadOnly, pDstMedium);
982 else if (cmd == CMD_FLOPPY)
983 rc = createMedium(a, strFormat.c_str(), pszDst, DeviceType_Floppy,
984 AccessMode_ReadWrite, pDstMedium);
985 if (FAILED(rc))
986 break;
987 }
988
989 ComPtr<IProgress> pProgress;
990 com::SafeArray<MediumVariant_T> l_variants(sizeof(MediumVariant_T)*8);
991
992 for (ULONG i = 0; i < l_variants.size(); ++i)
993 {
994 ULONG temp = enmMediumVariant;
995 temp &= 1<<i;
996 l_variants [i] = (MediumVariant_T)temp;
997 }
998
999 CHECK_ERROR_BREAK(pSrcMedium, CloneTo(pDstMedium, ComSafeArrayAsInParam(l_variants), NULL, pProgress.asOutParam()));
1000
1001 rc = showProgress(pProgress);
1002 CHECK_PROGRESS_ERROR_BREAK(pProgress, ("Failed to clone medium"));
1003
1004 Bstr uuid;
1005 CHECK_ERROR_BREAK(pDstMedium, COMGETTER(Id)(uuid.asOutParam()));
1006
1007 RTPrintf("Clone medium created in format '%ls'. UUID: %s\n",
1008 format.raw(), Utf8Str(uuid).c_str());
1009 }
1010 while (0);
1011
1012 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1013}
1014
1015static const RTGETOPTDEF g_aConvertFromRawHardDiskOptions[] =
1016{
1017 { "--format", 'o', RTGETOPT_REQ_STRING },
1018 { "-format", 'o', RTGETOPT_REQ_STRING },
1019 { "--static", 'F', RTGETOPT_REQ_NOTHING },
1020 { "-static", 'F', RTGETOPT_REQ_NOTHING },
1021 { "--variant", 'm', RTGETOPT_REQ_STRING },
1022 { "-variant", 'm', RTGETOPT_REQ_STRING },
1023 { "--uuid", 'u', RTGETOPT_REQ_STRING },
1024};
1025
1026RTEXITCODE handleConvertFromRaw(HandlerArg *a)
1027{
1028 int rc = VINF_SUCCESS;
1029 bool fReadFromStdIn = false;
1030 const char *format = "VDI";
1031 const char *srcfilename = NULL;
1032 const char *dstfilename = NULL;
1033 const char *filesize = NULL;
1034 unsigned uImageFlags = VD_IMAGE_FLAGS_NONE;
1035 void *pvBuf = NULL;
1036 RTUUID uuid;
1037 PCRTUUID pUuid = NULL;
1038
1039 int c;
1040 RTGETOPTUNION ValueUnion;
1041 RTGETOPTSTATE GetState;
1042 // start at 0 because main() has hacked both the argc and argv given to us
1043 RTGetOptInit(&GetState, a->argc, a->argv, g_aConvertFromRawHardDiskOptions, RT_ELEMENTS(g_aConvertFromRawHardDiskOptions),
1044 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1045 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1046 {
1047 switch (c)
1048 {
1049 case 'u': // --uuid
1050 if (RT_FAILURE(RTUuidFromStr(&uuid, ValueUnion.psz)))
1051 return errorSyntax(USAGE_CONVERTFROMRAW, "Invalid UUID '%s'", ValueUnion.psz);
1052 pUuid = &uuid;
1053 break;
1054 case 'o': // --format
1055 format = ValueUnion.psz;
1056 break;
1057
1058 case 'm': // --variant
1059 {
1060 MediumVariant_T enmMediumVariant = MediumVariant_Standard;
1061 rc = parseMediumVariant(ValueUnion.psz, &enmMediumVariant);
1062 if (RT_FAILURE(rc))
1063 return errorArgument("Invalid medium variant '%s'", ValueUnion.psz);
1064 /// @todo cleaner solution than assuming 1:1 mapping?
1065 uImageFlags = (unsigned)enmMediumVariant;
1066 break;
1067 }
1068 case VINF_GETOPT_NOT_OPTION:
1069 if (!srcfilename)
1070 {
1071 srcfilename = ValueUnion.psz;
1072 fReadFromStdIn = !strcmp(srcfilename, "stdin");
1073 }
1074 else if (!dstfilename)
1075 dstfilename = ValueUnion.psz;
1076 else if (fReadFromStdIn && !filesize)
1077 filesize = ValueUnion.psz;
1078 else
1079 return errorSyntax(USAGE_CONVERTFROMRAW, "Invalid parameter '%s'", ValueUnion.psz);
1080 break;
1081
1082 default:
1083 return errorGetOpt(USAGE_CONVERTFROMRAW, c, &ValueUnion);
1084 }
1085 }
1086
1087 if (!srcfilename || !dstfilename || (fReadFromStdIn && !filesize))
1088 return errorSyntax(USAGE_CONVERTFROMRAW, "Incorrect number of parameters");
1089 RTStrmPrintf(g_pStdErr, "Converting from raw image file=\"%s\" to file=\"%s\"...\n",
1090 srcfilename, dstfilename);
1091
1092 PVDISK pDisk = NULL;
1093
1094 PVDINTERFACE pVDIfs = NULL;
1095 VDINTERFACEERROR vdInterfaceError;
1096 vdInterfaceError.pfnError = handleVDError;
1097 vdInterfaceError.pfnMessage = NULL;
1098
1099 rc = VDInterfaceAdd(&vdInterfaceError.Core, "VBoxManage_IError", VDINTERFACETYPE_ERROR,
1100 NULL, sizeof(VDINTERFACEERROR), &pVDIfs);
1101 AssertRC(rc);
1102
1103 /* open raw image file. */
1104 RTFILE File;
1105 if (fReadFromStdIn)
1106 rc = RTFileFromNative(&File, RTFILE_NATIVE_STDIN);
1107 else
1108 rc = RTFileOpen(&File, srcfilename, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
1109 if (RT_FAILURE(rc))
1110 {
1111 RTMsgError("Cannot open file \"%s\": %Rrc", srcfilename, rc);
1112 goto out;
1113 }
1114
1115 uint64_t cbFile;
1116 /* get image size. */
1117 if (fReadFromStdIn)
1118 cbFile = RTStrToUInt64(filesize);
1119 else
1120 rc = RTFileGetSize(File, &cbFile);
1121 if (RT_FAILURE(rc))
1122 {
1123 RTMsgError("Cannot get image size for file \"%s\": %Rrc", srcfilename, rc);
1124 goto out;
1125 }
1126
1127 RTStrmPrintf(g_pStdErr, "Creating %s image with size %RU64 bytes (%RU64MB)...\n",
1128 (uImageFlags & VD_IMAGE_FLAGS_FIXED) ? "fixed" : "dynamic", cbFile, (cbFile + _1M - 1) / _1M);
1129 char pszComment[256];
1130 RTStrPrintf(pszComment, sizeof(pszComment), "Converted image from %s", srcfilename);
1131 rc = VDCreate(pVDIfs, VDTYPE_HDD, &pDisk);
1132 if (RT_FAILURE(rc))
1133 {
1134 RTMsgError("Cannot create the virtual disk container: %Rrc", rc);
1135 goto out;
1136 }
1137
1138 Assert(RT_MIN(cbFile / 512 / 16 / 63, 16383) -
1139 (unsigned int)RT_MIN(cbFile / 512 / 16 / 63, 16383) == 0);
1140 VDGEOMETRY PCHS, LCHS;
1141 PCHS.cCylinders = (unsigned int)RT_MIN(cbFile / 512 / 16 / 63, 16383);
1142 PCHS.cHeads = 16;
1143 PCHS.cSectors = 63;
1144 LCHS.cCylinders = 0;
1145 LCHS.cHeads = 0;
1146 LCHS.cSectors = 0;
1147 rc = VDCreateBase(pDisk, format, dstfilename, cbFile,
1148 uImageFlags, pszComment, &PCHS, &LCHS, pUuid,
1149 VD_OPEN_FLAGS_NORMAL, NULL, NULL);
1150 if (RT_FAILURE(rc))
1151 {
1152 RTMsgError("Cannot create the disk image \"%s\": %Rrc", dstfilename, rc);
1153 goto out;
1154 }
1155
1156 size_t cbBuffer;
1157 cbBuffer = _1M;
1158 pvBuf = RTMemAlloc(cbBuffer);
1159 if (!pvBuf)
1160 {
1161 rc = VERR_NO_MEMORY;
1162 RTMsgError("Out of memory allocating buffers for image \"%s\": %Rrc", dstfilename, rc);
1163 goto out;
1164 }
1165
1166 uint64_t offFile;
1167 offFile = 0;
1168 while (offFile < cbFile)
1169 {
1170 size_t cbRead;
1171 size_t cbToRead;
1172 cbRead = 0;
1173 cbToRead = cbFile - offFile >= (uint64_t)cbBuffer ?
1174 cbBuffer : (size_t)(cbFile - offFile);
1175 rc = RTFileRead(File, pvBuf, cbToRead, &cbRead);
1176 if (RT_FAILURE(rc) || !cbRead)
1177 break;
1178 rc = VDWrite(pDisk, offFile, pvBuf, cbRead);
1179 if (RT_FAILURE(rc))
1180 {
1181 RTMsgError("Failed to write to disk image \"%s\": %Rrc", dstfilename, rc);
1182 goto out;
1183 }
1184 offFile += cbRead;
1185 }
1186
1187out:
1188 if (pvBuf)
1189 RTMemFree(pvBuf);
1190 if (pDisk)
1191 VDClose(pDisk, RT_FAILURE(rc));
1192 if (File != NIL_RTFILE)
1193 RTFileClose(File);
1194
1195 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1196}
1197
1198HRESULT showMediumInfo(const ComPtr<IVirtualBox> &pVirtualBox,
1199 const ComPtr<IMedium> &pMedium,
1200 const char *pszParentUUID,
1201 bool fOptLong)
1202{
1203 HRESULT rc = S_OK;
1204 do
1205 {
1206 Bstr uuid;
1207 pMedium->COMGETTER(Id)(uuid.asOutParam());
1208 RTPrintf("UUID: %ls\n", uuid.raw());
1209 if (pszParentUUID)
1210 RTPrintf("Parent UUID: %s\n", pszParentUUID);
1211
1212 /* check for accessibility */
1213 MediumState_T enmState;
1214 CHECK_ERROR_BREAK(pMedium, RefreshState(&enmState));
1215 const char *pszState = "unknown";
1216 switch (enmState)
1217 {
1218 case MediumState_NotCreated:
1219 pszState = "not created";
1220 break;
1221 case MediumState_Created:
1222 pszState = "created";
1223 break;
1224 case MediumState_LockedRead:
1225 pszState = "locked read";
1226 break;
1227 case MediumState_LockedWrite:
1228 pszState = "locked write";
1229 break;
1230 case MediumState_Inaccessible:
1231 pszState = "inaccessible";
1232 break;
1233 case MediumState_Creating:
1234 pszState = "creating";
1235 break;
1236 case MediumState_Deleting:
1237 pszState = "deleting";
1238 break;
1239 }
1240 RTPrintf("State: %s\n", pszState);
1241
1242 if (fOptLong && enmState == MediumState_Inaccessible)
1243 {
1244 Bstr err;
1245 CHECK_ERROR_BREAK(pMedium, COMGETTER(LastAccessError)(err.asOutParam()));
1246 RTPrintf("Access Error: %ls\n", err.raw());
1247 }
1248
1249 if (fOptLong)
1250 {
1251 Bstr description;
1252 pMedium->COMGETTER(Description)(description.asOutParam());
1253 if (!description.isEmpty())
1254 RTPrintf("Description: %ls\n", description.raw());
1255 }
1256
1257 MediumType_T type;
1258 pMedium->COMGETTER(Type)(&type);
1259 const char *typeStr = "unknown";
1260 switch (type)
1261 {
1262 case MediumType_Normal:
1263 if (pszParentUUID && Guid(pszParentUUID).isValid())
1264 typeStr = "normal (differencing)";
1265 else
1266 typeStr = "normal (base)";
1267 break;
1268 case MediumType_Immutable:
1269 typeStr = "immutable";
1270 break;
1271 case MediumType_Writethrough:
1272 typeStr = "writethrough";
1273 break;
1274 case MediumType_Shareable:
1275 typeStr = "shareable";
1276 break;
1277 case MediumType_Readonly:
1278 typeStr = "readonly";
1279 break;
1280 case MediumType_MultiAttach:
1281 typeStr = "multiattach";
1282 break;
1283 }
1284 RTPrintf("Type: %s\n", typeStr);
1285
1286 /* print out information specific for differencing media */
1287 if (fOptLong && pszParentUUID && Guid(pszParentUUID).isValid())
1288 {
1289 BOOL autoReset = FALSE;
1290 pMedium->COMGETTER(AutoReset)(&autoReset);
1291 RTPrintf("Auto-Reset: %s\n", autoReset ? "on" : "off");
1292 }
1293
1294 Bstr loc;
1295 pMedium->COMGETTER(Location)(loc.asOutParam());
1296 RTPrintf("Location: %ls\n", loc.raw());
1297
1298 Bstr format;
1299 pMedium->COMGETTER(Format)(format.asOutParam());
1300 RTPrintf("Storage format: %ls\n", format.raw());
1301
1302 if (fOptLong)
1303 {
1304 com::SafeArray<MediumVariant_T> safeArray_variant;
1305
1306 pMedium->COMGETTER(Variant)(ComSafeArrayAsOutParam(safeArray_variant));
1307 ULONG variant=0;
1308 for (size_t i = 0; i < safeArray_variant.size(); i++)
1309 variant |= safeArray_variant[i];
1310
1311 const char *variantStr = "unknown";
1312 switch (variant & ~(MediumVariant_Fixed | MediumVariant_Diff))
1313 {
1314 case MediumVariant_VmdkSplit2G:
1315 variantStr = "split2G";
1316 break;
1317 case MediumVariant_VmdkStreamOptimized:
1318 variantStr = "streamOptimized";
1319 break;
1320 case MediumVariant_VmdkESX:
1321 variantStr = "ESX";
1322 break;
1323 case MediumVariant_Standard:
1324 variantStr = "default";
1325 break;
1326 }
1327 const char *variantTypeStr = "dynamic";
1328 if (variant & MediumVariant_Fixed)
1329 variantTypeStr = "fixed";
1330 else if (variant & MediumVariant_Diff)
1331 variantTypeStr = "differencing";
1332 RTPrintf("Format variant: %s %s\n", variantTypeStr, variantStr);
1333 }
1334
1335 LONG64 logicalSize;
1336 pMedium->COMGETTER(LogicalSize)(&logicalSize);
1337 RTPrintf("Capacity: %lld MBytes\n", logicalSize >> 20);
1338 if (fOptLong)
1339 {
1340 LONG64 actualSize;
1341 pMedium->COMGETTER(Size)(&actualSize);
1342 RTPrintf("Size on disk: %lld MBytes\n", actualSize >> 20);
1343 }
1344
1345 Bstr strCipher;
1346 Bstr strPasswordId;
1347 HRESULT rc2 = pMedium->GetEncryptionSettings(strCipher.asOutParam(), strPasswordId.asOutParam());
1348 if (SUCCEEDED(rc2))
1349 {
1350 RTPrintf("Encryption: enabled\n");
1351 if (fOptLong)
1352 {
1353 RTPrintf("Cipher: %ls\n", strCipher.raw());
1354 RTPrintf("Password ID: %ls\n", strPasswordId.raw());
1355 }
1356 }
1357 else
1358 RTPrintf("Encryption: disabled\n");
1359
1360 if (fOptLong)
1361 {
1362 com::SafeArray<BSTR> names;
1363 com::SafeArray<BSTR> values;
1364 pMedium->GetProperties(Bstr().raw(), ComSafeArrayAsOutParam(names), ComSafeArrayAsOutParam(values));
1365 size_t cNames = names.size();
1366 size_t cValues = values.size();
1367 bool fFirst = true;
1368 for (size_t i = 0; i < cNames; i++)
1369 {
1370 Bstr value;
1371 if (i < cValues)
1372 value = values[i];
1373 RTPrintf("%s%ls=%ls\n",
1374 fFirst ? "Property: " : " ",
1375 names[i], value.raw());
1376 }
1377 }
1378
1379 if (fOptLong)
1380 {
1381 bool fFirst = true;
1382 com::SafeArray<BSTR> machineIds;
1383 pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(machineIds));
1384 for (size_t i = 0; i < machineIds.size(); i++)
1385 {
1386 ComPtr<IMachine> pMachine;
1387 CHECK_ERROR(pVirtualBox, FindMachine(machineIds[i], pMachine.asOutParam()));
1388 if (pMachine)
1389 {
1390 Bstr name;
1391 pMachine->COMGETTER(Name)(name.asOutParam());
1392 pMachine->COMGETTER(Id)(uuid.asOutParam());
1393 RTPrintf("%s%ls (UUID: %ls)",
1394 fFirst ? "In use by VMs: " : " ",
1395 name.raw(), machineIds[i]);
1396 fFirst = false;
1397 com::SafeArray<BSTR> snapshotIds;
1398 pMedium->GetSnapshotIds(machineIds[i],
1399 ComSafeArrayAsOutParam(snapshotIds));
1400 for (size_t j = 0; j < snapshotIds.size(); j++)
1401 {
1402 ComPtr<ISnapshot> pSnapshot;
1403 pMachine->FindSnapshot(snapshotIds[j], pSnapshot.asOutParam());
1404 if (pSnapshot)
1405 {
1406 Bstr snapshotName;
1407 pSnapshot->COMGETTER(Name)(snapshotName.asOutParam());
1408 RTPrintf(" [%ls (UUID: %ls)]", snapshotName.raw(), snapshotIds[j]);
1409 }
1410 }
1411 RTPrintf("\n");
1412 }
1413 }
1414 }
1415
1416 if (fOptLong)
1417 {
1418 com::SafeIfaceArray<IMedium> children;
1419 pMedium->COMGETTER(Children)(ComSafeArrayAsOutParam(children));
1420 bool fFirst = true;
1421 for (size_t i = 0; i < children.size(); i++)
1422 {
1423 ComPtr<IMedium> pChild(children[i]);
1424 if (pChild)
1425 {
1426 Bstr childUUID;
1427 pChild->COMGETTER(Id)(childUUID.asOutParam());
1428 RTPrintf("%s%ls\n",
1429 fFirst ? "Child UUIDs: " : " ",
1430 childUUID.raw());
1431 fFirst = false;
1432 }
1433 }
1434 }
1435 }
1436 while (0);
1437
1438 return rc;
1439}
1440
1441static const RTGETOPTDEF g_aShowMediumInfoOptions[] =
1442{
1443 { "disk", 'd', RTGETOPT_REQ_NOTHING },
1444 { "dvd", 'D', RTGETOPT_REQ_NOTHING },
1445 { "floppy", 'f', RTGETOPT_REQ_NOTHING },
1446};
1447
1448RTEXITCODE handleShowMediumInfo(HandlerArg *a)
1449{
1450 enum {
1451 CMD_NONE,
1452 CMD_DISK,
1453 CMD_DVD,
1454 CMD_FLOPPY
1455 } cmd = CMD_NONE;
1456 const char *pszFilenameOrUuid = NULL;
1457
1458 int c;
1459 RTGETOPTUNION ValueUnion;
1460 RTGETOPTSTATE GetState;
1461 // start at 0 because main() has hacked both the argc and argv given to us
1462 RTGetOptInit(&GetState, a->argc, a->argv, g_aShowMediumInfoOptions, RT_ELEMENTS(g_aShowMediumInfoOptions),
1463 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1464 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1465 {
1466 switch (c)
1467 {
1468 case 'd': // disk
1469 if (cmd != CMD_NONE)
1470 return errorSyntax(USAGE_SHOWMEDIUMINFO, "Only one command can be specified: '%s'", ValueUnion.psz);
1471 cmd = CMD_DISK;
1472 break;
1473
1474 case 'D': // DVD
1475 if (cmd != CMD_NONE)
1476 return errorSyntax(USAGE_SHOWMEDIUMINFO, "Only one command can be specified: '%s'", ValueUnion.psz);
1477 cmd = CMD_DVD;
1478 break;
1479
1480 case 'f': // floppy
1481 if (cmd != CMD_NONE)
1482 return errorSyntax(USAGE_SHOWMEDIUMINFO, "Only one command can be specified: '%s'", ValueUnion.psz);
1483 cmd = CMD_FLOPPY;
1484 break;
1485
1486 case VINF_GETOPT_NOT_OPTION:
1487 if (!pszFilenameOrUuid)
1488 pszFilenameOrUuid = ValueUnion.psz;
1489 else
1490 return errorSyntax(USAGE_SHOWMEDIUMINFO, "Invalid parameter '%s'", ValueUnion.psz);
1491 break;
1492
1493 default:
1494 if (c > 0)
1495 {
1496 if (RT_C_IS_PRINT(c))
1497 return errorSyntax(USAGE_SHOWMEDIUMINFO, "Invalid option -%c", c);
1498 else
1499 return errorSyntax(USAGE_SHOWMEDIUMINFO, "Invalid option case %i", c);
1500 }
1501 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1502 return errorSyntax(USAGE_SHOWMEDIUMINFO, "unknown option: %s\n", ValueUnion.psz);
1503 else if (ValueUnion.pDef)
1504 return errorSyntax(USAGE_SHOWMEDIUMINFO, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1505 else
1506 return errorSyntax(USAGE_SHOWMEDIUMINFO, "error: %Rrs", c);
1507 }
1508 }
1509
1510 if (cmd == CMD_NONE)
1511 cmd = CMD_DISK;
1512
1513 /* check for required options */
1514 if (!pszFilenameOrUuid)
1515 return errorSyntax(USAGE_SHOWMEDIUMINFO, "Medium name or UUID required");
1516
1517 HRESULT rc = S_OK; /* Prevents warning. */
1518
1519 ComPtr<IMedium> pMedium;
1520 if (cmd == CMD_DISK)
1521 rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
1522 AccessMode_ReadOnly, pMedium,
1523 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1524 else if (cmd == CMD_DVD)
1525 rc = openMedium(a, pszFilenameOrUuid, DeviceType_DVD,
1526 AccessMode_ReadOnly, pMedium,
1527 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1528 else if (cmd == CMD_FLOPPY)
1529 rc = openMedium(a, pszFilenameOrUuid, DeviceType_Floppy,
1530 AccessMode_ReadOnly, pMedium,
1531 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1532 if (FAILED(rc))
1533 return RTEXITCODE_FAILURE;
1534
1535 Utf8Str strParentUUID("base");
1536 ComPtr<IMedium> pParent;
1537 pMedium->COMGETTER(Parent)(pParent.asOutParam());
1538 if (!pParent.isNull())
1539 {
1540 Bstr bstrParentUUID;
1541 pParent->COMGETTER(Id)(bstrParentUUID.asOutParam());
1542 strParentUUID = bstrParentUUID;
1543 }
1544
1545 rc = showMediumInfo(a->virtualBox, pMedium, strParentUUID.c_str(), true);
1546
1547 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1548}
1549
1550static const RTGETOPTDEF g_aCloseMediumOptions[] =
1551{
1552 { "disk", 'd', RTGETOPT_REQ_NOTHING },
1553 { "dvd", 'D', RTGETOPT_REQ_NOTHING },
1554 { "floppy", 'f', RTGETOPT_REQ_NOTHING },
1555 { "--delete", 'r', RTGETOPT_REQ_NOTHING },
1556};
1557
1558RTEXITCODE handleCloseMedium(HandlerArg *a)
1559{
1560 HRESULT rc = S_OK;
1561 enum {
1562 CMD_NONE,
1563 CMD_DISK,
1564 CMD_DVD,
1565 CMD_FLOPPY
1566 } cmd = CMD_NONE;
1567 const char *pszFilenameOrUuid = NULL;
1568 bool fDelete = false;
1569
1570 int c;
1571 RTGETOPTUNION ValueUnion;
1572 RTGETOPTSTATE GetState;
1573 // start at 0 because main() has hacked both the argc and argv given to us
1574 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloseMediumOptions, RT_ELEMENTS(g_aCloseMediumOptions),
1575 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1576 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1577 {
1578 switch (c)
1579 {
1580 case 'd': // disk
1581 if (cmd != CMD_NONE)
1582 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1583 cmd = CMD_DISK;
1584 break;
1585
1586 case 'D': // DVD
1587 if (cmd != CMD_NONE)
1588 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1589 cmd = CMD_DVD;
1590 break;
1591
1592 case 'f': // floppy
1593 if (cmd != CMD_NONE)
1594 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1595 cmd = CMD_FLOPPY;
1596 break;
1597
1598 case 'r': // --delete
1599 fDelete = true;
1600 break;
1601
1602 case VINF_GETOPT_NOT_OPTION:
1603 if (!pszFilenameOrUuid)
1604 pszFilenameOrUuid = ValueUnion.psz;
1605 else
1606 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
1607 break;
1608
1609 default:
1610 if (c > 0)
1611 {
1612 if (RT_C_IS_PRINT(c))
1613 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option -%c", c);
1614 else
1615 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option case %i", c);
1616 }
1617 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1618 return errorSyntax(USAGE_CLOSEMEDIUM, "unknown option: %s\n", ValueUnion.psz);
1619 else if (ValueUnion.pDef)
1620 return errorSyntax(USAGE_CLOSEMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1621 else
1622 return errorSyntax(USAGE_CLOSEMEDIUM, "error: %Rrs", c);
1623 }
1624 }
1625
1626 /* check for required options */
1627 if (cmd == CMD_NONE)
1628 cmd = CMD_DISK;
1629 if (!pszFilenameOrUuid)
1630 return errorSyntax(USAGE_CLOSEMEDIUM, "Medium name or UUID required");
1631
1632 ComPtr<IMedium> pMedium;
1633 if (cmd == CMD_DISK)
1634 rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
1635 AccessMode_ReadWrite, pMedium,
1636 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1637 else if (cmd == CMD_DVD)
1638 rc = openMedium(a, pszFilenameOrUuid, DeviceType_DVD,
1639 AccessMode_ReadOnly, pMedium,
1640 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1641 else if (cmd == CMD_FLOPPY)
1642 rc = openMedium(a, pszFilenameOrUuid, DeviceType_Floppy,
1643 AccessMode_ReadWrite, pMedium,
1644 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1645
1646 if (SUCCEEDED(rc) && pMedium)
1647 {
1648 if (fDelete)
1649 {
1650 ComPtr<IProgress> pProgress;
1651 CHECK_ERROR(pMedium, DeleteStorage(pProgress.asOutParam()));
1652 if (SUCCEEDED(rc))
1653 {
1654 rc = showProgress(pProgress);
1655 CHECK_PROGRESS_ERROR(pProgress, ("Failed to delete medium"));
1656 }
1657 else
1658 RTMsgError("Failed to delete medium. Error code %Rrc", rc);
1659 }
1660 CHECK_ERROR(pMedium, Close());
1661 }
1662
1663 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1664}
1665
1666RTEXITCODE handleMediumProperty(HandlerArg *a)
1667{
1668 HRESULT rc = S_OK;
1669 const char *pszCmd = NULL;
1670 enum {
1671 CMD_NONE,
1672 CMD_DISK,
1673 CMD_DVD,
1674 CMD_FLOPPY
1675 } cmd = CMD_NONE;
1676 const char *pszAction = NULL;
1677 const char *pszFilenameOrUuid = NULL;
1678 const char *pszProperty = NULL;
1679 ComPtr<IMedium> pMedium;
1680
1681 pszCmd = (a->argc > 0) ? a->argv[0] : "";
1682 if ( !RTStrICmp(pszCmd, "disk")
1683 || !RTStrICmp(pszCmd, "dvd")
1684 || !RTStrICmp(pszCmd, "floppy"))
1685 {
1686 if (!RTStrICmp(pszCmd, "disk"))
1687 cmd = CMD_DISK;
1688 else if (!RTStrICmp(pszCmd, "dvd"))
1689 cmd = CMD_DVD;
1690 else if (!RTStrICmp(pszCmd, "floppy"))
1691 cmd = CMD_FLOPPY;
1692 else
1693 {
1694 AssertMsgFailed(("unexpected parameter %s\n", pszCmd));
1695 cmd = CMD_DISK;
1696 }
1697 a->argv++;
1698 a->argc--;
1699 }
1700 else
1701 {
1702 pszCmd = NULL;
1703 cmd = CMD_DISK;
1704 }
1705
1706 if (a->argc == 0)
1707 return errorSyntax(USAGE_MEDIUMPROPERTY, "Missing action");
1708
1709 pszAction = a->argv[0];
1710 if ( RTStrICmp(pszAction, "set")
1711 && RTStrICmp(pszAction, "get")
1712 && RTStrICmp(pszAction, "delete"))
1713 return errorSyntax(USAGE_MEDIUMPROPERTY, "Invalid action given: %s", pszAction);
1714
1715 if ( ( !RTStrICmp(pszAction, "set")
1716 && a->argc != 4)
1717 || ( RTStrICmp(pszAction, "set")
1718 && a->argc != 3))
1719 return errorSyntax(USAGE_MEDIUMPROPERTY, "Invalid number of arguments given for action: %s", pszAction);
1720
1721 pszFilenameOrUuid = a->argv[1];
1722 pszProperty = a->argv[2];
1723
1724 if (cmd == CMD_DISK)
1725 rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
1726 AccessMode_ReadWrite, pMedium,
1727 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1728 else if (cmd == CMD_DVD)
1729 rc = openMedium(a, pszFilenameOrUuid, DeviceType_DVD,
1730 AccessMode_ReadOnly, pMedium,
1731 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1732 else if (cmd == CMD_FLOPPY)
1733 rc = openMedium(a, pszFilenameOrUuid, DeviceType_Floppy,
1734 AccessMode_ReadWrite, pMedium,
1735 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1736 if (SUCCEEDED(rc) && !pMedium.isNull())
1737 {
1738 if (!RTStrICmp(pszAction, "set"))
1739 {
1740 const char *pszValue = a->argv[3];
1741 CHECK_ERROR(pMedium, SetProperty(Bstr(pszProperty).raw(), Bstr(pszValue).raw()));
1742 }
1743 else if (!RTStrICmp(pszAction, "get"))
1744 {
1745 Bstr strVal;
1746 CHECK_ERROR(pMedium, GetProperty(Bstr(pszProperty).raw(), strVal.asOutParam()));
1747 if (SUCCEEDED(rc))
1748 RTPrintf("%s=%ls\n", pszProperty, strVal.raw());
1749 }
1750 else if (!RTStrICmp(pszAction, "delete"))
1751 {
1752 CHECK_ERROR(pMedium, SetProperty(Bstr(pszProperty).raw(), Bstr().raw()));
1753 /** @todo */
1754 }
1755 }
1756
1757 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1758}
1759
1760static const RTGETOPTDEF g_aEncryptMediumOptions[] =
1761{
1762 { "--newpassword", 'n', RTGETOPT_REQ_STRING },
1763 { "--oldpassword", 'o', RTGETOPT_REQ_STRING },
1764 { "--cipher", 'c', RTGETOPT_REQ_STRING },
1765 { "--newpasswordid", 'i', RTGETOPT_REQ_STRING }
1766};
1767
1768RTEXITCODE handleEncryptMedium(HandlerArg *a)
1769{
1770 HRESULT rc;
1771 ComPtr<IMedium> hardDisk;
1772 const char *pszPasswordNew = NULL;
1773 const char *pszPasswordOld = NULL;
1774 const char *pszCipher = NULL;
1775 const char *pszFilenameOrUuid = NULL;
1776 const char *pszNewPasswordId = NULL;
1777 Utf8Str strPasswordNew;
1778 Utf8Str strPasswordOld;
1779
1780 int c;
1781 RTGETOPTUNION ValueUnion;
1782 RTGETOPTSTATE GetState;
1783 // start at 0 because main() has hacked both the argc and argv given to us
1784 RTGetOptInit(&GetState, a->argc, a->argv, g_aEncryptMediumOptions, RT_ELEMENTS(g_aEncryptMediumOptions),
1785 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1786 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1787 {
1788 switch (c)
1789 {
1790 case 'n': // --newpassword
1791 pszPasswordNew = ValueUnion.psz;
1792 break;
1793
1794 case 'o': // --oldpassword
1795 pszPasswordOld = ValueUnion.psz;
1796 break;
1797
1798 case 'c': // --cipher
1799 pszCipher = ValueUnion.psz;
1800 break;
1801
1802 case 'i': // --newpasswordid
1803 pszNewPasswordId = ValueUnion.psz;
1804 break;
1805
1806 case VINF_GETOPT_NOT_OPTION:
1807 if (!pszFilenameOrUuid)
1808 pszFilenameOrUuid = ValueUnion.psz;
1809 else
1810 return errorSyntax(USAGE_ENCRYPTMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
1811 break;
1812
1813 default:
1814 if (c > 0)
1815 {
1816 if (RT_C_IS_PRINT(c))
1817 return errorSyntax(USAGE_ENCRYPTMEDIUM, "Invalid option -%c", c);
1818 else
1819 return errorSyntax(USAGE_ENCRYPTMEDIUM, "Invalid option case %i", c);
1820 }
1821 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1822 return errorSyntax(USAGE_ENCRYPTMEDIUM, "unknown option: %s\n", ValueUnion.psz);
1823 else if (ValueUnion.pDef)
1824 return errorSyntax(USAGE_ENCRYPTMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1825 else
1826 return errorSyntax(USAGE_ENCRYPTMEDIUM, "error: %Rrs", c);
1827 }
1828 }
1829
1830 if (!pszFilenameOrUuid)
1831 return errorSyntax(USAGE_ENCRYPTMEDIUM, "Disk name or UUID required");
1832
1833 if (!pszPasswordNew && !pszPasswordOld)
1834 return errorSyntax(USAGE_ENCRYPTMEDIUM, "No password specified");
1835
1836 if ( (pszPasswordNew && !pszNewPasswordId)
1837 || (!pszPasswordNew && pszNewPasswordId))
1838 return errorSyntax(USAGE_ENCRYPTMEDIUM, "A new password must always have a valid identifier set at the same time");
1839
1840 if (pszPasswordNew)
1841 {
1842 if (!RTStrCmp(pszPasswordNew, "-"))
1843 {
1844 /* Get password from console. */
1845 RTEXITCODE rcExit = readPasswordFromConsole(&strPasswordNew, "Enter new password:");
1846 if (rcExit == RTEXITCODE_FAILURE)
1847 return rcExit;
1848 }
1849 else
1850 {
1851 RTEXITCODE rcExit = readPasswordFile(pszPasswordNew, &strPasswordNew);
1852 if (rcExit == RTEXITCODE_FAILURE)
1853 {
1854 RTMsgError("Failed to read new password from file");
1855 return rcExit;
1856 }
1857 }
1858 }
1859
1860 if (pszPasswordOld)
1861 {
1862 if (!RTStrCmp(pszPasswordOld, "-"))
1863 {
1864 /* Get password from console. */
1865 RTEXITCODE rcExit = readPasswordFromConsole(&strPasswordOld, "Enter old password:");
1866 if (rcExit == RTEXITCODE_FAILURE)
1867 return rcExit;
1868 }
1869 else
1870 {
1871 RTEXITCODE rcExit = readPasswordFile(pszPasswordOld, &strPasswordOld);
1872 if (rcExit == RTEXITCODE_FAILURE)
1873 {
1874 RTMsgError("Failed to read old password from file");
1875 return rcExit;
1876 }
1877 }
1878 }
1879
1880 /* Always open the medium if necessary, there is no other way. */
1881 rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
1882 AccessMode_ReadWrite, hardDisk,
1883 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1884 if (FAILED(rc))
1885 return RTEXITCODE_FAILURE;
1886 if (hardDisk.isNull())
1887 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid hard disk reference, avoiding crash");
1888
1889 ComPtr<IProgress> progress;
1890 CHECK_ERROR(hardDisk, ChangeEncryption(Bstr(strPasswordOld).raw(), Bstr(pszCipher).raw(),
1891 Bstr(strPasswordNew).raw(), Bstr(pszNewPasswordId).raw(),
1892 progress.asOutParam()));
1893 if (SUCCEEDED(rc))
1894 rc = showProgress(progress);
1895 if (FAILED(rc))
1896 {
1897 if (rc == E_NOTIMPL)
1898 RTMsgError("Encrypt hard disk operation is not implemented!");
1899 else if (rc == VBOX_E_NOT_SUPPORTED)
1900 RTMsgError("Encrypt hard disk operation for this cipher is not implemented yet!");
1901 else if (!progress.isNull())
1902 CHECK_PROGRESS_ERROR(progress, ("Failed to encrypt hard disk"));
1903 else
1904 RTMsgError("Failed to encrypt hard disk!");
1905 }
1906
1907 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1908}
1909
1910RTEXITCODE handleCheckMediumPassword(HandlerArg *a)
1911{
1912 HRESULT rc;
1913 ComPtr<IMedium> hardDisk;
1914 const char *pszFilenameOrUuid = NULL;
1915 Utf8Str strPassword;
1916
1917 if (a->argc != 2)
1918 return errorSyntax(USAGE_MEDIUMENCCHKPWD, "Invalid number of arguments: %d", a->argc);
1919
1920 pszFilenameOrUuid = a->argv[0];
1921
1922 if (!RTStrCmp(a->argv[1], "-"))
1923 {
1924 /* Get password from console. */
1925 RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, "Enter password:");
1926 if (rcExit == RTEXITCODE_FAILURE)
1927 return rcExit;
1928 }
1929 else
1930 {
1931 RTEXITCODE rcExit = readPasswordFile(a->argv[1], &strPassword);
1932 if (rcExit == RTEXITCODE_FAILURE)
1933 {
1934 RTMsgError("Failed to read password from file");
1935 return rcExit;
1936 }
1937 }
1938
1939 /* Always open the medium if necessary, there is no other way. */
1940 rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
1941 AccessMode_ReadWrite, hardDisk,
1942 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1943 if (FAILED(rc))
1944 return RTEXITCODE_FAILURE;
1945 if (hardDisk.isNull())
1946 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid hard disk reference, avoiding crash");
1947
1948 CHECK_ERROR(hardDisk, CheckEncryptionPassword(Bstr(strPassword).raw()));
1949 if (SUCCEEDED(rc))
1950 RTPrintf("The given password is correct\n");
1951 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1952}
1953
1954
1955/*********************************************************************************************************************************
1956* The mediumio command *
1957*********************************************************************************************************************************/
1958
1959/**
1960 * Common MediumIO options.
1961 */
1962typedef struct MEDIUMIOCOMMONOPT
1963{
1964 const char *pszFilenameOrUuid;
1965 DeviceType_T enmDeviceType;
1966 const char *pszPasswordFile;
1967} MEDIUMIOCOMMONOPT;
1968typedef MEDIUMIOCOMMONOPT *PMEDIUMIOCOMMONOPT;
1969typedef MEDIUMIOCOMMONOPT const *PCMEDIUMIOCOMMONOPT;
1970
1971/* For RTGETOPTDEF array initializer. */
1972#define MEDIUMIOCOMMONOPT_DEFS() \
1973 { "--disk", 'd', RTGETOPT_REQ_STRING }, \
1974 { "--harddisk", 'd', RTGETOPT_REQ_STRING }, \
1975 { "disk", 'd', RTGETOPT_REQ_STRING }, \
1976 { "harddisk", 'd', RTGETOPT_REQ_STRING }, \
1977 { "--dvd", 'D', RTGETOPT_REQ_STRING }, \
1978 { "--iso", 'D', RTGETOPT_REQ_STRING }, \
1979 { "dvd", 'D', RTGETOPT_REQ_STRING }, \
1980 { "iso", 'D', RTGETOPT_REQ_STRING }, \
1981 { "--floppy", 'f', RTGETOPT_REQ_STRING }, \
1982 { "floppy", 'f', RTGETOPT_REQ_STRING }, \
1983 { "--password-file", 'P', RTGETOPT_REQ_STRING }
1984
1985/* For option switch. */
1986#define MEDIUMIOCOMMONOPT_CASES(a_pCommonOpts) \
1987 case 'd': \
1988 (a_pCommonOpts)->enmDeviceType = DeviceType_HardDisk; \
1989 (a_pCommonOpts)->pszFilenameOrUuid = ValueUnion.psz; \
1990 break; \
1991 case 'D': \
1992 (a_pCommonOpts)->enmDeviceType = DeviceType_DVD; \
1993 (a_pCommonOpts)->pszFilenameOrUuid = ValueUnion.psz; \
1994 break; \
1995 case 'f': \
1996 (a_pCommonOpts)->enmDeviceType = DeviceType_Floppy; \
1997 (a_pCommonOpts)->pszFilenameOrUuid = ValueUnion.psz; \
1998 break; \
1999 case 'P': \
2000 (a_pCommonOpts)->pszPasswordFile = ValueUnion.psz; \
2001 break
2002
2003
2004/**
2005 * Worker for mediumio operations that returns a IMediumIO for the specified
2006 * medium.
2007 *
2008 * @returns Exit code.
2009 * @param pHandler The handler state structure (for IVirtualBox).
2010 * @param pCommonOpts Common mediumio options.
2011 * @param fWritable Whether to open writable (true) or read only
2012 * (false).
2013 * @param rPtrMediumIO Where to return the IMediumIO pointer.
2014 * @param pcbMedium Where to return the meidum size. Optional.
2015 */
2016static RTEXITCODE mediumIOOpenMediumForIO(HandlerArg *pHandler, PCMEDIUMIOCOMMONOPT pCommonOpts, bool fWritable,
2017 ComPtr<IMediumIO> &rPtrMediumIO, uint64_t *pcbMedium = NULL)
2018{
2019 /* Clear returns. */
2020 if (pcbMedium)
2021 *pcbMedium = 0;
2022 rPtrMediumIO.setNull();
2023
2024 /*
2025 * Make sure a medium was specified already.
2026 */
2027 if (pCommonOpts->enmDeviceType == MEDIUMCATEGORY_NONE)
2028 return errorSyntax("No medium specified!");
2029
2030 /*
2031 * Read the password.
2032 */
2033 Bstr bstrPassword;
2034 if (pCommonOpts->pszPasswordFile)
2035 {
2036 Utf8Str strPassword;
2037 RTEXITCODE rcExit;
2038 if (pCommonOpts->pszPasswordFile[0] == '-' && pCommonOpts->pszPasswordFile[1] == '\0')
2039 rcExit = readPasswordFromConsole(&strPassword, "Enter encryption password:");
2040 else
2041 rcExit = readPasswordFile(pCommonOpts->pszPasswordFile, &strPassword);
2042 if (rcExit != RTEXITCODE_SUCCESS)
2043 return rcExit;
2044 bstrPassword = strPassword;
2045 strPassword.assign(strPassword.length(), '*');
2046 }
2047
2048 /*
2049 * Open the medium and then get I/O access to it.
2050 */
2051 ComPtr<IMedium> ptrMedium;
2052 HRESULT hrc = openMedium(pHandler, pCommonOpts->pszFilenameOrUuid, pCommonOpts->enmDeviceType,
2053 fWritable ? AccessMode_ReadWrite : AccessMode_ReadOnly,
2054 ptrMedium, false /* fForceNewUuidOnOpen */, false /* fSilent */);
2055 if (SUCCEEDED(hrc))
2056 CHECK_ERROR2I_STMT(ptrMedium, OpenForIO(fWritable, bstrPassword.raw(), rPtrMediumIO.asOutParam()), hrc = hrcCheck);
2057
2058 /*
2059 * If the size is requested get it after we've opened it.
2060 */
2061 if (pcbMedium && SUCCEEDED(hrc))
2062 {
2063 LONG64 cbLogical = 0;
2064 CHECK_ERROR2I_STMT(ptrMedium, COMGETTER(LogicalSize)(&cbLogical), hrc = hrcCheck);
2065 *pcbMedium = cbLogical;
2066 if (!SUCCEEDED(hrc))
2067 rPtrMediumIO.setNull();
2068 }
2069
2070 memset(bstrPassword.mutableRaw(), '*', bstrPassword.length() * sizeof(RTUTF16));
2071 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2072}
2073
2074
2075/**
2076 * mediumio formatfat
2077 */
2078static RTEXITCODE handleMediumIOFormatFat(HandlerArg *a, int iFirst, PMEDIUMIOCOMMONOPT pCommonOpts)
2079{
2080 /*
2081 * Parse the options.
2082 */
2083 bool fQuick = false;
2084 static const RTGETOPTDEF s_aOptions[] =
2085 {
2086 MEDIUMIOCOMMONOPT_DEFS(),
2087 { "--quick", 'q', RTGETOPT_REQ_NOTHING },
2088 };
2089
2090 RTGETOPTSTATE GetState;
2091 int rc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2092 AssertRC(rc);
2093 RTGETOPTUNION ValueUnion;
2094 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
2095 {
2096 switch (rc)
2097 {
2098 MEDIUMIOCOMMONOPT_CASES(pCommonOpts);
2099
2100 case 'q':
2101 fQuick = true;
2102 break;
2103
2104 default:
2105 return errorGetOpt(rc, &ValueUnion);
2106 }
2107 }
2108
2109 /*
2110 * Open the medium for I/O and format it.
2111 */
2112 ComPtr<IMediumIO> ptrMediumIO;
2113 RTEXITCODE rcExit = mediumIOOpenMediumForIO(a, pCommonOpts, true /*fWritable*/, ptrMediumIO);
2114 if (rcExit != RTEXITCODE_SUCCESS)
2115 return rcExit;
2116 CHECK_ERROR2I_RET(ptrMediumIO, FormatFAT(fQuick), RTEXITCODE_FAILURE);
2117 return RTEXITCODE_SUCCESS;
2118}
2119
2120/**
2121 * mediumio cat
2122 */
2123static RTEXITCODE handleMediumIOCat(HandlerArg *a, int iFirst, PMEDIUMIOCOMMONOPT pCommonOpts)
2124{
2125 /*
2126 * Parse the options.
2127 */
2128 static const RTGETOPTDEF s_aOptions[] =
2129 {
2130 MEDIUMIOCOMMONOPT_DEFS(),
2131 { "--hex", 'H', RTGETOPT_REQ_NOTHING },
2132 { "--offset", 'o', RTGETOPT_REQ_UINT64 },
2133 { "--output", 'O', RTGETOPT_REQ_UINT64 },
2134 { "--size", 's', RTGETOPT_REQ_UINT64 },
2135 };
2136 bool fHex = false;
2137 uint64_t off = 0;
2138 const char *pszOutput = NULL;
2139 uint64_t cb = UINT64_MAX;
2140
2141 RTGETOPTSTATE GetState;
2142 int rc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2143 AssertRC(rc);
2144 RTGETOPTUNION ValueUnion;
2145 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
2146 {
2147 switch (rc)
2148 {
2149 MEDIUMIOCOMMONOPT_CASES(pCommonOpts);
2150
2151 case 'H':
2152 fHex = true;
2153 break;
2154
2155 case 'o':
2156 off = ValueUnion.u64;
2157 break;
2158
2159 case 'O':
2160 pszOutput = ValueUnion.psz;
2161 break;
2162
2163 case 's':
2164 cb = ValueUnion.u64;
2165 break;
2166
2167 default:
2168 return errorGetOpt(rc, &ValueUnion);
2169 }
2170 }
2171
2172 /*
2173 * Open the medium for I/O.
2174 */
2175 ComPtr<IMediumIO> ptrMediumIO;
2176 uint64_t cbMedium;
2177 RTEXITCODE rcExit = mediumIOOpenMediumForIO(a, pCommonOpts, false /*fWritable*/, ptrMediumIO, &cbMedium);
2178 if (rcExit == RTEXITCODE_SUCCESS)
2179 {
2180 /*
2181 * Do we have an output file or do we write to stdout?
2182 */
2183 PRTSTREAM pOut = NULL;
2184 if (pszOutput && (pszOutput[0] != '-' || pszOutput[1] != '\0'))
2185 {
2186 int vrc = RTStrmOpen(pszOutput, fHex ? "wt" : "wb", &pOut);
2187 if (RT_FAILURE(vrc))
2188 rcExit = RTMsgErrorExitFailure("Error opening '%s' for writing: %Rrc", pszOutput, vrc);
2189 }
2190 else
2191 {
2192 pOut = g_pStdOut;
2193 if (!fHex)
2194 RTStrmSetMode(pOut, true, -1);
2195 }
2196
2197 if (rcExit == RTEXITCODE_SUCCESS)
2198 {
2199 /*
2200 * Adjust 'cb' now that we've got the medium size.
2201 */
2202 if (off >= cbMedium)
2203 {
2204 RTMsgWarning("Specified offset (%#RX64) is beyond the end of the medium (%#RX64)", off, cbMedium);
2205 cb = 0;
2206 }
2207 else if ( cb > cbMedium
2208 || cb + off > cbMedium)
2209 cb = cbMedium - off;
2210
2211 /*
2212 * Hex dump preps. (The duplication detection is making ASSUMPTIONS about
2213 * all the reads being a multiple of cchWidth, except for the final one.)
2214 */
2215 char abHexBuf[16] = { 0 };
2216 size_t cbHexBuf = 0;
2217 unsigned const cchWidth = RT_ELEMENTS(abHexBuf);
2218 uint64_t const offEndDupCheck = cb - cchWidth;
2219 uint64_t cDuplicates = 0;
2220
2221 /*
2222 * Do the reading.
2223 */
2224 while (cb > 0)
2225 {
2226 char szLine[32 + cchWidth * 4 + 32];
2227
2228 /* Do the reading. */
2229 uint32_t const cbToRead = (uint32_t)RT_MIN(cb, _128K);
2230 SafeArray<BYTE> SafeArrayBuf;
2231 HRESULT hrc = ptrMediumIO->Read(off, cbToRead, ComSafeArrayAsOutParam(SafeArrayBuf));
2232 if (FAILED(hrc))
2233 {
2234 RTStrPrintf(szLine, sizeof(szLine), "Read(%zu bytes at %#RX64)", cbToRead, off);
2235 com::GlueHandleComError(ptrMediumIO, szLine, hrc, __FILE__, __LINE__);
2236 break;
2237 }
2238
2239 /* Output the data. */
2240 size_t const cbReturned = SafeArrayBuf.size();
2241 if (cbReturned)
2242 {
2243 BYTE const *pbBuf = SafeArrayBuf.raw();
2244 int vrc;
2245 if (!fHex)
2246 vrc = RTStrmWrite(pOut, pbBuf, cbReturned);
2247 else
2248 {
2249 /* hexdump -C */
2250 uint64_t offHex = off;
2251 uint64_t const offHexEnd = off + cb;
2252 while (offHex < offHexEnd)
2253 {
2254 if ( offHex >= offEndDupCheck
2255 || cbHexBuf == 0
2256 || memcmp(pbBuf, abHexBuf, cchWidth) != 0
2257 || ( cDuplicates == 0
2258 && ( offHex + cchWidth >= offEndDupCheck
2259 || memcmp(pbBuf + cchWidth, pbBuf, cchWidth) != 0)) )
2260 {
2261 if (cDuplicates > 0)
2262 {
2263 RTStrmPrintf(pOut, "********** <ditto x %RU64>\n", cDuplicates);
2264 cDuplicates = 0;
2265 }
2266
2267 size_t cch = RTStrPrintf(szLine, sizeof(szLine), "%012RX64:", offHex);
2268 unsigned i;
2269 for (i = 0; i < cchWidth && offHex + i < offHexEnd; i++)
2270 {
2271 static const char s_szHexDigits[17] = "0123456789abcdef";
2272 szLine[cch++] = (i & 7) || i == 0 ? ' ' : '-';
2273 uint8_t const u8 = pbBuf[i];
2274 szLine[cch++] = s_szHexDigits[u8 >> 4];
2275 szLine[cch++] = s_szHexDigits[u8 & 0xf];
2276 }
2277 while (i++ < cchWidth)
2278 {
2279 szLine[cch++] = ' ';
2280 szLine[cch++] = ' ';
2281 szLine[cch++] = ' ';
2282 }
2283 szLine[cch++] = ' ';
2284
2285 for (i = 0; i < cchWidth && offHex + i < offHexEnd; i++)
2286 {
2287 uint8_t const u8 = pbBuf[i];
2288 szLine[cch++] = u8 < 127 && u8 >= 32 ? u8 : '.';
2289 }
2290 szLine[cch++] = '\n';
2291 szLine[cch] = '\0';
2292
2293 vrc = RTStrmWrite(pOut, szLine, cch);
2294 if (RT_FAILURE(vrc))
2295 break;
2296
2297
2298 /* copy bytes over to the duplication detection buffer. */
2299 cbHexBuf = (size_t)RT_MIN(cchWidth, offHexEnd - offHex);
2300 memcpy(abHexBuf, pbBuf, cbHexBuf);
2301 }
2302 else
2303 cDuplicates++;
2304
2305 /* Advance to next line. */
2306 pbBuf += cchWidth;
2307 offHex += cchWidth;
2308 }
2309 }
2310 if (RT_FAILURE(vrc))
2311 {
2312 rcExit = RTMsgErrorExitFailure("Error writing to '%s': %Rrc", pszOutput, vrc);
2313 break;
2314 }
2315 }
2316
2317 /* Advance. */
2318 if (cbReturned != cbToRead)
2319 {
2320 rcExit = RTMsgErrorExitFailure("Expected read() at offset %RU64 (%#RX64) to return %#zx bytes, only got %#zx!\n",
2321 off, off, cbReturned, cbToRead);
2322 break;
2323 }
2324 off += cbReturned;
2325 cb -= cbReturned;
2326 }
2327
2328 /*
2329 * Close output.
2330 */
2331 if (pOut != g_pStdOut)
2332 {
2333 int vrc = RTStrmClose(pOut);
2334 if (RT_FAILURE(vrc))
2335 rcExit = RTMsgErrorExitFailure("Error closing '%s': %Rrc", pszOutput, vrc);
2336 }
2337 else if (!fHex)
2338 RTStrmSetMode(pOut, false, -1);
2339 }
2340 }
2341 return rcExit;
2342}
2343
2344
2345RTEXITCODE handleMediumIO(HandlerArg *a)
2346{
2347 /*
2348 * Parse image-option and sub-command.
2349 */
2350 static const RTGETOPTDEF s_aOptions[] =
2351 {
2352 MEDIUMIOCOMMONOPT_DEFS(),
2353 /* sub-commands */
2354 { "formatfat", 1000, RTGETOPT_REQ_NOTHING },
2355 { "cat", 1001, RTGETOPT_REQ_NOTHING },
2356 };
2357 MEDIUMIOCOMMONOPT CommonOpts = { NULL, DeviceType_Null, NULL };
2358
2359 RTGETOPTSTATE GetState;
2360 int rc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
2361 AssertRC(rc);
2362 RTGETOPTUNION ValueUnion;
2363 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
2364 {
2365 switch (rc)
2366 {
2367 MEDIUMIOCOMMONOPT_CASES(&CommonOpts);
2368
2369 /* Sub-commands: */
2370 case 1000:
2371 setCurrentSubcommand(HELP_SCOPE_MEDIUMIO_FORMATFAT);
2372 return handleMediumIOFormatFat(a, GetState.iNext, &CommonOpts);
2373 case 1001:
2374 setCurrentSubcommand(HELP_SCOPE_MEDIUMIO_CAT);
2375 return handleMediumIOCat(a, GetState.iNext, &CommonOpts);
2376
2377 case VINF_GETOPT_NOT_OPTION:
2378 return errorUnknownSubcommand(ValueUnion.psz);
2379
2380 default:
2381 return errorGetOpt(rc, &ValueUnion);
2382 }
2383 }
2384 return errorNoSubcommand();
2385}
2386
2387#endif /* !VBOX_ONLY_DOCS */
Note: See TracBrowser for help on using the repository browser.

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