VirtualBox

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

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

Main/CloudProviderManager+CloudProvider+CloudProfile: Introduce CloudProfile as separate interface, and do a big cleanup. Adding synchronization and incomplete support for moving to an extension pack. Updated VBoxManage to list providers and touched up the GUI code slightly to deal with the changed interfaces.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 85.1 KB
Line 
1/* $Id: VBoxManageDisk.cpp 73716 2018-08-16 15:58:57Z 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 != MediumVariant_Standard))
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#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
1240 case MediumState_32BitHack: break; /* Shut up compiler warnings. */
1241#endif
1242 }
1243 RTPrintf("State: %s\n", pszState);
1244
1245 if (fOptLong && enmState == MediumState_Inaccessible)
1246 {
1247 Bstr err;
1248 CHECK_ERROR_BREAK(pMedium, COMGETTER(LastAccessError)(err.asOutParam()));
1249 RTPrintf("Access Error: %ls\n", err.raw());
1250 }
1251
1252 if (fOptLong)
1253 {
1254 Bstr description;
1255 pMedium->COMGETTER(Description)(description.asOutParam());
1256 if (!description.isEmpty())
1257 RTPrintf("Description: %ls\n", description.raw());
1258 }
1259
1260 MediumType_T type;
1261 pMedium->COMGETTER(Type)(&type);
1262 const char *typeStr = "unknown";
1263 switch (type)
1264 {
1265 case MediumType_Normal:
1266 if (pszParentUUID && Guid(pszParentUUID).isValid())
1267 typeStr = "normal (differencing)";
1268 else
1269 typeStr = "normal (base)";
1270 break;
1271 case MediumType_Immutable:
1272 typeStr = "immutable";
1273 break;
1274 case MediumType_Writethrough:
1275 typeStr = "writethrough";
1276 break;
1277 case MediumType_Shareable:
1278 typeStr = "shareable";
1279 break;
1280 case MediumType_Readonly:
1281 typeStr = "readonly";
1282 break;
1283 case MediumType_MultiAttach:
1284 typeStr = "multiattach";
1285 break;
1286#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
1287 case MediumType_32BitHack: break; /* Shut up compiler warnings. */
1288#endif
1289 }
1290 RTPrintf("Type: %s\n", typeStr);
1291
1292 /* print out information specific for differencing media */
1293 if (fOptLong && pszParentUUID && Guid(pszParentUUID).isValid())
1294 {
1295 BOOL autoReset = FALSE;
1296 pMedium->COMGETTER(AutoReset)(&autoReset);
1297 RTPrintf("Auto-Reset: %s\n", autoReset ? "on" : "off");
1298 }
1299
1300 Bstr loc;
1301 pMedium->COMGETTER(Location)(loc.asOutParam());
1302 RTPrintf("Location: %ls\n", loc.raw());
1303
1304 Bstr format;
1305 pMedium->COMGETTER(Format)(format.asOutParam());
1306 RTPrintf("Storage format: %ls\n", format.raw());
1307
1308 if (fOptLong)
1309 {
1310 com::SafeArray<MediumVariant_T> safeArray_variant;
1311
1312 pMedium->COMGETTER(Variant)(ComSafeArrayAsOutParam(safeArray_variant));
1313 ULONG variant=0;
1314 for (size_t i = 0; i < safeArray_variant.size(); i++)
1315 variant |= safeArray_variant[i];
1316
1317 const char *variantStr = "unknown";
1318 switch (variant & ~(MediumVariant_Fixed | MediumVariant_Diff))
1319 {
1320 case MediumVariant_VmdkSplit2G:
1321 variantStr = "split2G";
1322 break;
1323 case MediumVariant_VmdkStreamOptimized:
1324 variantStr = "streamOptimized";
1325 break;
1326 case MediumVariant_VmdkESX:
1327 variantStr = "ESX";
1328 break;
1329 case MediumVariant_Standard:
1330 variantStr = "default";
1331 break;
1332 }
1333 const char *variantTypeStr = "dynamic";
1334 if (variant & MediumVariant_Fixed)
1335 variantTypeStr = "fixed";
1336 else if (variant & MediumVariant_Diff)
1337 variantTypeStr = "differencing";
1338 RTPrintf("Format variant: %s %s\n", variantTypeStr, variantStr);
1339 }
1340
1341 LONG64 logicalSize;
1342 pMedium->COMGETTER(LogicalSize)(&logicalSize);
1343 RTPrintf("Capacity: %lld MBytes\n", logicalSize >> 20);
1344 if (fOptLong)
1345 {
1346 LONG64 actualSize;
1347 pMedium->COMGETTER(Size)(&actualSize);
1348 RTPrintf("Size on disk: %lld MBytes\n", actualSize >> 20);
1349 }
1350
1351 Bstr strCipher;
1352 Bstr strPasswordId;
1353 HRESULT rc2 = pMedium->GetEncryptionSettings(strCipher.asOutParam(), strPasswordId.asOutParam());
1354 if (SUCCEEDED(rc2))
1355 {
1356 RTPrintf("Encryption: enabled\n");
1357 if (fOptLong)
1358 {
1359 RTPrintf("Cipher: %ls\n", strCipher.raw());
1360 RTPrintf("Password ID: %ls\n", strPasswordId.raw());
1361 }
1362 }
1363 else
1364 RTPrintf("Encryption: disabled\n");
1365
1366 if (fOptLong)
1367 {
1368 com::SafeArray<BSTR> names;
1369 com::SafeArray<BSTR> values;
1370 pMedium->GetProperties(Bstr().raw(), ComSafeArrayAsOutParam(names), ComSafeArrayAsOutParam(values));
1371 size_t cNames = names.size();
1372 size_t cValues = values.size();
1373 bool fFirst = true;
1374 for (size_t i = 0; i < cNames; i++)
1375 {
1376 Bstr value;
1377 if (i < cValues)
1378 value = values[i];
1379 RTPrintf("%s%ls=%ls\n",
1380 fFirst ? "Property: " : " ",
1381 names[i], value.raw());
1382 fFirst = false;
1383 }
1384 }
1385
1386 if (fOptLong)
1387 {
1388 bool fFirst = true;
1389 com::SafeArray<BSTR> machineIds;
1390 pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(machineIds));
1391 for (size_t i = 0; i < machineIds.size(); i++)
1392 {
1393 ComPtr<IMachine> pMachine;
1394 CHECK_ERROR(pVirtualBox, FindMachine(machineIds[i], pMachine.asOutParam()));
1395 if (pMachine)
1396 {
1397 Bstr name;
1398 pMachine->COMGETTER(Name)(name.asOutParam());
1399 pMachine->COMGETTER(Id)(uuid.asOutParam());
1400 RTPrintf("%s%ls (UUID: %ls)",
1401 fFirst ? "In use by VMs: " : " ",
1402 name.raw(), machineIds[i]);
1403 fFirst = false;
1404 com::SafeArray<BSTR> snapshotIds;
1405 pMedium->GetSnapshotIds(machineIds[i],
1406 ComSafeArrayAsOutParam(snapshotIds));
1407 for (size_t j = 0; j < snapshotIds.size(); j++)
1408 {
1409 ComPtr<ISnapshot> pSnapshot;
1410 pMachine->FindSnapshot(snapshotIds[j], pSnapshot.asOutParam());
1411 if (pSnapshot)
1412 {
1413 Bstr snapshotName;
1414 pSnapshot->COMGETTER(Name)(snapshotName.asOutParam());
1415 RTPrintf(" [%ls (UUID: %ls)]", snapshotName.raw(), snapshotIds[j]);
1416 }
1417 }
1418 RTPrintf("\n");
1419 }
1420 }
1421 }
1422
1423 if (fOptLong)
1424 {
1425 com::SafeIfaceArray<IMedium> children;
1426 pMedium->COMGETTER(Children)(ComSafeArrayAsOutParam(children));
1427 bool fFirst = true;
1428 for (size_t i = 0; i < children.size(); i++)
1429 {
1430 ComPtr<IMedium> pChild(children[i]);
1431 if (pChild)
1432 {
1433 Bstr childUUID;
1434 pChild->COMGETTER(Id)(childUUID.asOutParam());
1435 RTPrintf("%s%ls\n",
1436 fFirst ? "Child UUIDs: " : " ",
1437 childUUID.raw());
1438 fFirst = false;
1439 }
1440 }
1441 }
1442 }
1443 while (0);
1444
1445 return rc;
1446}
1447
1448static const RTGETOPTDEF g_aShowMediumInfoOptions[] =
1449{
1450 { "disk", 'd', RTGETOPT_REQ_NOTHING },
1451 { "dvd", 'D', RTGETOPT_REQ_NOTHING },
1452 { "floppy", 'f', RTGETOPT_REQ_NOTHING },
1453};
1454
1455RTEXITCODE handleShowMediumInfo(HandlerArg *a)
1456{
1457 enum {
1458 CMD_NONE,
1459 CMD_DISK,
1460 CMD_DVD,
1461 CMD_FLOPPY
1462 } cmd = CMD_NONE;
1463 const char *pszFilenameOrUuid = NULL;
1464
1465 int c;
1466 RTGETOPTUNION ValueUnion;
1467 RTGETOPTSTATE GetState;
1468 // start at 0 because main() has hacked both the argc and argv given to us
1469 RTGetOptInit(&GetState, a->argc, a->argv, g_aShowMediumInfoOptions, RT_ELEMENTS(g_aShowMediumInfoOptions),
1470 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1471 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1472 {
1473 switch (c)
1474 {
1475 case 'd': // disk
1476 if (cmd != CMD_NONE)
1477 return errorSyntax(USAGE_SHOWMEDIUMINFO, "Only one command can be specified: '%s'", ValueUnion.psz);
1478 cmd = CMD_DISK;
1479 break;
1480
1481 case 'D': // DVD
1482 if (cmd != CMD_NONE)
1483 return errorSyntax(USAGE_SHOWMEDIUMINFO, "Only one command can be specified: '%s'", ValueUnion.psz);
1484 cmd = CMD_DVD;
1485 break;
1486
1487 case 'f': // floppy
1488 if (cmd != CMD_NONE)
1489 return errorSyntax(USAGE_SHOWMEDIUMINFO, "Only one command can be specified: '%s'", ValueUnion.psz);
1490 cmd = CMD_FLOPPY;
1491 break;
1492
1493 case VINF_GETOPT_NOT_OPTION:
1494 if (!pszFilenameOrUuid)
1495 pszFilenameOrUuid = ValueUnion.psz;
1496 else
1497 return errorSyntax(USAGE_SHOWMEDIUMINFO, "Invalid parameter '%s'", ValueUnion.psz);
1498 break;
1499
1500 default:
1501 if (c > 0)
1502 {
1503 if (RT_C_IS_PRINT(c))
1504 return errorSyntax(USAGE_SHOWMEDIUMINFO, "Invalid option -%c", c);
1505 else
1506 return errorSyntax(USAGE_SHOWMEDIUMINFO, "Invalid option case %i", c);
1507 }
1508 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1509 return errorSyntax(USAGE_SHOWMEDIUMINFO, "unknown option: %s\n", ValueUnion.psz);
1510 else if (ValueUnion.pDef)
1511 return errorSyntax(USAGE_SHOWMEDIUMINFO, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1512 else
1513 return errorSyntax(USAGE_SHOWMEDIUMINFO, "error: %Rrs", c);
1514 }
1515 }
1516
1517 if (cmd == CMD_NONE)
1518 cmd = CMD_DISK;
1519
1520 /* check for required options */
1521 if (!pszFilenameOrUuid)
1522 return errorSyntax(USAGE_SHOWMEDIUMINFO, "Medium name or UUID required");
1523
1524 HRESULT rc = S_OK; /* Prevents warning. */
1525
1526 ComPtr<IMedium> pMedium;
1527 if (cmd == CMD_DISK)
1528 rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
1529 AccessMode_ReadOnly, pMedium,
1530 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1531 else if (cmd == CMD_DVD)
1532 rc = openMedium(a, pszFilenameOrUuid, DeviceType_DVD,
1533 AccessMode_ReadOnly, pMedium,
1534 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1535 else if (cmd == CMD_FLOPPY)
1536 rc = openMedium(a, pszFilenameOrUuid, DeviceType_Floppy,
1537 AccessMode_ReadOnly, pMedium,
1538 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1539 if (FAILED(rc))
1540 return RTEXITCODE_FAILURE;
1541
1542 Utf8Str strParentUUID("base");
1543 ComPtr<IMedium> pParent;
1544 pMedium->COMGETTER(Parent)(pParent.asOutParam());
1545 if (!pParent.isNull())
1546 {
1547 Bstr bstrParentUUID;
1548 pParent->COMGETTER(Id)(bstrParentUUID.asOutParam());
1549 strParentUUID = bstrParentUUID;
1550 }
1551
1552 rc = showMediumInfo(a->virtualBox, pMedium, strParentUUID.c_str(), true);
1553
1554 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1555}
1556
1557static const RTGETOPTDEF g_aCloseMediumOptions[] =
1558{
1559 { "disk", 'd', RTGETOPT_REQ_NOTHING },
1560 { "dvd", 'D', RTGETOPT_REQ_NOTHING },
1561 { "floppy", 'f', RTGETOPT_REQ_NOTHING },
1562 { "--delete", 'r', RTGETOPT_REQ_NOTHING },
1563};
1564
1565RTEXITCODE handleCloseMedium(HandlerArg *a)
1566{
1567 HRESULT rc = S_OK;
1568 enum {
1569 CMD_NONE,
1570 CMD_DISK,
1571 CMD_DVD,
1572 CMD_FLOPPY
1573 } cmd = CMD_NONE;
1574 const char *pszFilenameOrUuid = NULL;
1575 bool fDelete = false;
1576
1577 int c;
1578 RTGETOPTUNION ValueUnion;
1579 RTGETOPTSTATE GetState;
1580 // start at 0 because main() has hacked both the argc and argv given to us
1581 RTGetOptInit(&GetState, a->argc, a->argv, g_aCloseMediumOptions, RT_ELEMENTS(g_aCloseMediumOptions),
1582 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1583 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1584 {
1585 switch (c)
1586 {
1587 case 'd': // disk
1588 if (cmd != CMD_NONE)
1589 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1590 cmd = CMD_DISK;
1591 break;
1592
1593 case 'D': // DVD
1594 if (cmd != CMD_NONE)
1595 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1596 cmd = CMD_DVD;
1597 break;
1598
1599 case 'f': // floppy
1600 if (cmd != CMD_NONE)
1601 return errorSyntax(USAGE_CLOSEMEDIUM, "Only one command can be specified: '%s'", ValueUnion.psz);
1602 cmd = CMD_FLOPPY;
1603 break;
1604
1605 case 'r': // --delete
1606 fDelete = true;
1607 break;
1608
1609 case VINF_GETOPT_NOT_OPTION:
1610 if (!pszFilenameOrUuid)
1611 pszFilenameOrUuid = ValueUnion.psz;
1612 else
1613 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
1614 break;
1615
1616 default:
1617 if (c > 0)
1618 {
1619 if (RT_C_IS_PRINT(c))
1620 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option -%c", c);
1621 else
1622 return errorSyntax(USAGE_CLOSEMEDIUM, "Invalid option case %i", c);
1623 }
1624 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1625 return errorSyntax(USAGE_CLOSEMEDIUM, "unknown option: %s\n", ValueUnion.psz);
1626 else if (ValueUnion.pDef)
1627 return errorSyntax(USAGE_CLOSEMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1628 else
1629 return errorSyntax(USAGE_CLOSEMEDIUM, "error: %Rrs", c);
1630 }
1631 }
1632
1633 /* check for required options */
1634 if (cmd == CMD_NONE)
1635 cmd = CMD_DISK;
1636 if (!pszFilenameOrUuid)
1637 return errorSyntax(USAGE_CLOSEMEDIUM, "Medium name or UUID required");
1638
1639 ComPtr<IMedium> pMedium;
1640 if (cmd == CMD_DISK)
1641 rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
1642 AccessMode_ReadWrite, pMedium,
1643 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1644 else if (cmd == CMD_DVD)
1645 rc = openMedium(a, pszFilenameOrUuid, DeviceType_DVD,
1646 AccessMode_ReadOnly, pMedium,
1647 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1648 else if (cmd == CMD_FLOPPY)
1649 rc = openMedium(a, pszFilenameOrUuid, DeviceType_Floppy,
1650 AccessMode_ReadWrite, pMedium,
1651 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1652
1653 if (SUCCEEDED(rc) && pMedium)
1654 {
1655 if (fDelete)
1656 {
1657 ComPtr<IProgress> pProgress;
1658 CHECK_ERROR(pMedium, DeleteStorage(pProgress.asOutParam()));
1659 if (SUCCEEDED(rc))
1660 {
1661 rc = showProgress(pProgress);
1662 CHECK_PROGRESS_ERROR(pProgress, ("Failed to delete medium"));
1663 }
1664 else
1665 RTMsgError("Failed to delete medium. Error code %Rrc", rc);
1666 }
1667 CHECK_ERROR(pMedium, Close());
1668 }
1669
1670 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1671}
1672
1673RTEXITCODE handleMediumProperty(HandlerArg *a)
1674{
1675 HRESULT rc = S_OK;
1676 const char *pszCmd = NULL;
1677 enum {
1678 CMD_NONE,
1679 CMD_DISK,
1680 CMD_DVD,
1681 CMD_FLOPPY
1682 } cmd = CMD_NONE;
1683 const char *pszAction = NULL;
1684 const char *pszFilenameOrUuid = NULL;
1685 const char *pszProperty = NULL;
1686 ComPtr<IMedium> pMedium;
1687
1688 pszCmd = (a->argc > 0) ? a->argv[0] : "";
1689 if ( !RTStrICmp(pszCmd, "disk")
1690 || !RTStrICmp(pszCmd, "dvd")
1691 || !RTStrICmp(pszCmd, "floppy"))
1692 {
1693 if (!RTStrICmp(pszCmd, "disk"))
1694 cmd = CMD_DISK;
1695 else if (!RTStrICmp(pszCmd, "dvd"))
1696 cmd = CMD_DVD;
1697 else if (!RTStrICmp(pszCmd, "floppy"))
1698 cmd = CMD_FLOPPY;
1699 else
1700 {
1701 AssertMsgFailed(("unexpected parameter %s\n", pszCmd));
1702 cmd = CMD_DISK;
1703 }
1704 a->argv++;
1705 a->argc--;
1706 }
1707 else
1708 {
1709 pszCmd = NULL;
1710 cmd = CMD_DISK;
1711 }
1712
1713 if (a->argc == 0)
1714 return errorSyntax(USAGE_MEDIUMPROPERTY, "Missing action");
1715
1716 pszAction = a->argv[0];
1717 if ( RTStrICmp(pszAction, "set")
1718 && RTStrICmp(pszAction, "get")
1719 && RTStrICmp(pszAction, "delete"))
1720 return errorSyntax(USAGE_MEDIUMPROPERTY, "Invalid action given: %s", pszAction);
1721
1722 if ( ( !RTStrICmp(pszAction, "set")
1723 && a->argc != 4)
1724 || ( RTStrICmp(pszAction, "set")
1725 && a->argc != 3))
1726 return errorSyntax(USAGE_MEDIUMPROPERTY, "Invalid number of arguments given for action: %s", pszAction);
1727
1728 pszFilenameOrUuid = a->argv[1];
1729 pszProperty = a->argv[2];
1730
1731 if (cmd == CMD_DISK)
1732 rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
1733 AccessMode_ReadWrite, pMedium,
1734 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1735 else if (cmd == CMD_DVD)
1736 rc = openMedium(a, pszFilenameOrUuid, DeviceType_DVD,
1737 AccessMode_ReadOnly, pMedium,
1738 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1739 else if (cmd == CMD_FLOPPY)
1740 rc = openMedium(a, pszFilenameOrUuid, DeviceType_Floppy,
1741 AccessMode_ReadWrite, pMedium,
1742 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1743 if (SUCCEEDED(rc) && !pMedium.isNull())
1744 {
1745 if (!RTStrICmp(pszAction, "set"))
1746 {
1747 const char *pszValue = a->argv[3];
1748 CHECK_ERROR(pMedium, SetProperty(Bstr(pszProperty).raw(), Bstr(pszValue).raw()));
1749 }
1750 else if (!RTStrICmp(pszAction, "get"))
1751 {
1752 Bstr strVal;
1753 CHECK_ERROR(pMedium, GetProperty(Bstr(pszProperty).raw(), strVal.asOutParam()));
1754 if (SUCCEEDED(rc))
1755 RTPrintf("%s=%ls\n", pszProperty, strVal.raw());
1756 }
1757 else if (!RTStrICmp(pszAction, "delete"))
1758 {
1759 CHECK_ERROR(pMedium, SetProperty(Bstr(pszProperty).raw(), Bstr().raw()));
1760 /** @todo */
1761 }
1762 }
1763
1764 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1765}
1766
1767static const RTGETOPTDEF g_aEncryptMediumOptions[] =
1768{
1769 { "--newpassword", 'n', RTGETOPT_REQ_STRING },
1770 { "--oldpassword", 'o', RTGETOPT_REQ_STRING },
1771 { "--cipher", 'c', RTGETOPT_REQ_STRING },
1772 { "--newpasswordid", 'i', RTGETOPT_REQ_STRING }
1773};
1774
1775RTEXITCODE handleEncryptMedium(HandlerArg *a)
1776{
1777 HRESULT rc;
1778 ComPtr<IMedium> hardDisk;
1779 const char *pszPasswordNew = NULL;
1780 const char *pszPasswordOld = NULL;
1781 const char *pszCipher = NULL;
1782 const char *pszFilenameOrUuid = NULL;
1783 const char *pszNewPasswordId = NULL;
1784 Utf8Str strPasswordNew;
1785 Utf8Str strPasswordOld;
1786
1787 int c;
1788 RTGETOPTUNION ValueUnion;
1789 RTGETOPTSTATE GetState;
1790 // start at 0 because main() has hacked both the argc and argv given to us
1791 RTGetOptInit(&GetState, a->argc, a->argv, g_aEncryptMediumOptions, RT_ELEMENTS(g_aEncryptMediumOptions),
1792 0, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
1793 while ((c = RTGetOpt(&GetState, &ValueUnion)))
1794 {
1795 switch (c)
1796 {
1797 case 'n': // --newpassword
1798 pszPasswordNew = ValueUnion.psz;
1799 break;
1800
1801 case 'o': // --oldpassword
1802 pszPasswordOld = ValueUnion.psz;
1803 break;
1804
1805 case 'c': // --cipher
1806 pszCipher = ValueUnion.psz;
1807 break;
1808
1809 case 'i': // --newpasswordid
1810 pszNewPasswordId = ValueUnion.psz;
1811 break;
1812
1813 case VINF_GETOPT_NOT_OPTION:
1814 if (!pszFilenameOrUuid)
1815 pszFilenameOrUuid = ValueUnion.psz;
1816 else
1817 return errorSyntax(USAGE_ENCRYPTMEDIUM, "Invalid parameter '%s'", ValueUnion.psz);
1818 break;
1819
1820 default:
1821 if (c > 0)
1822 {
1823 if (RT_C_IS_PRINT(c))
1824 return errorSyntax(USAGE_ENCRYPTMEDIUM, "Invalid option -%c", c);
1825 else
1826 return errorSyntax(USAGE_ENCRYPTMEDIUM, "Invalid option case %i", c);
1827 }
1828 else if (c == VERR_GETOPT_UNKNOWN_OPTION)
1829 return errorSyntax(USAGE_ENCRYPTMEDIUM, "unknown option: %s\n", ValueUnion.psz);
1830 else if (ValueUnion.pDef)
1831 return errorSyntax(USAGE_ENCRYPTMEDIUM, "%s: %Rrs", ValueUnion.pDef->pszLong, c);
1832 else
1833 return errorSyntax(USAGE_ENCRYPTMEDIUM, "error: %Rrs", c);
1834 }
1835 }
1836
1837 if (!pszFilenameOrUuid)
1838 return errorSyntax(USAGE_ENCRYPTMEDIUM, "Disk name or UUID required");
1839
1840 if (!pszPasswordNew && !pszPasswordOld)
1841 return errorSyntax(USAGE_ENCRYPTMEDIUM, "No password specified");
1842
1843 if ( (pszPasswordNew && !pszNewPasswordId)
1844 || (!pszPasswordNew && pszNewPasswordId))
1845 return errorSyntax(USAGE_ENCRYPTMEDIUM, "A new password must always have a valid identifier set at the same time");
1846
1847 if (pszPasswordNew)
1848 {
1849 if (!RTStrCmp(pszPasswordNew, "-"))
1850 {
1851 /* Get password from console. */
1852 RTEXITCODE rcExit = readPasswordFromConsole(&strPasswordNew, "Enter new password:");
1853 if (rcExit == RTEXITCODE_FAILURE)
1854 return rcExit;
1855 }
1856 else
1857 {
1858 RTEXITCODE rcExit = readPasswordFile(pszPasswordNew, &strPasswordNew);
1859 if (rcExit == RTEXITCODE_FAILURE)
1860 {
1861 RTMsgError("Failed to read new password from file");
1862 return rcExit;
1863 }
1864 }
1865 }
1866
1867 if (pszPasswordOld)
1868 {
1869 if (!RTStrCmp(pszPasswordOld, "-"))
1870 {
1871 /* Get password from console. */
1872 RTEXITCODE rcExit = readPasswordFromConsole(&strPasswordOld, "Enter old password:");
1873 if (rcExit == RTEXITCODE_FAILURE)
1874 return rcExit;
1875 }
1876 else
1877 {
1878 RTEXITCODE rcExit = readPasswordFile(pszPasswordOld, &strPasswordOld);
1879 if (rcExit == RTEXITCODE_FAILURE)
1880 {
1881 RTMsgError("Failed to read old password from file");
1882 return rcExit;
1883 }
1884 }
1885 }
1886
1887 /* Always open the medium if necessary, there is no other way. */
1888 rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
1889 AccessMode_ReadWrite, hardDisk,
1890 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1891 if (FAILED(rc))
1892 return RTEXITCODE_FAILURE;
1893 if (hardDisk.isNull())
1894 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid hard disk reference, avoiding crash");
1895
1896 ComPtr<IProgress> progress;
1897 CHECK_ERROR(hardDisk, ChangeEncryption(Bstr(strPasswordOld).raw(), Bstr(pszCipher).raw(),
1898 Bstr(strPasswordNew).raw(), Bstr(pszNewPasswordId).raw(),
1899 progress.asOutParam()));
1900 if (SUCCEEDED(rc))
1901 rc = showProgress(progress);
1902 if (FAILED(rc))
1903 {
1904 if (rc == E_NOTIMPL)
1905 RTMsgError("Encrypt hard disk operation is not implemented!");
1906 else if (rc == VBOX_E_NOT_SUPPORTED)
1907 RTMsgError("Encrypt hard disk operation for this cipher is not implemented yet!");
1908 else if (!progress.isNull())
1909 CHECK_PROGRESS_ERROR(progress, ("Failed to encrypt hard disk"));
1910 else
1911 RTMsgError("Failed to encrypt hard disk!");
1912 }
1913
1914 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1915}
1916
1917RTEXITCODE handleCheckMediumPassword(HandlerArg *a)
1918{
1919 HRESULT rc;
1920 ComPtr<IMedium> hardDisk;
1921 const char *pszFilenameOrUuid = NULL;
1922 Utf8Str strPassword;
1923
1924 if (a->argc != 2)
1925 return errorSyntax(USAGE_MEDIUMENCCHKPWD, "Invalid number of arguments: %d", a->argc);
1926
1927 pszFilenameOrUuid = a->argv[0];
1928
1929 if (!RTStrCmp(a->argv[1], "-"))
1930 {
1931 /* Get password from console. */
1932 RTEXITCODE rcExit = readPasswordFromConsole(&strPassword, "Enter password:");
1933 if (rcExit == RTEXITCODE_FAILURE)
1934 return rcExit;
1935 }
1936 else
1937 {
1938 RTEXITCODE rcExit = readPasswordFile(a->argv[1], &strPassword);
1939 if (rcExit == RTEXITCODE_FAILURE)
1940 {
1941 RTMsgError("Failed to read password from file");
1942 return rcExit;
1943 }
1944 }
1945
1946 /* Always open the medium if necessary, there is no other way. */
1947 rc = openMedium(a, pszFilenameOrUuid, DeviceType_HardDisk,
1948 AccessMode_ReadWrite, hardDisk,
1949 false /* fForceNewUuidOnOpen */, false /* fSilent */);
1950 if (FAILED(rc))
1951 return RTEXITCODE_FAILURE;
1952 if (hardDisk.isNull())
1953 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid hard disk reference, avoiding crash");
1954
1955 CHECK_ERROR(hardDisk, CheckEncryptionPassword(Bstr(strPassword).raw()));
1956 if (SUCCEEDED(rc))
1957 RTPrintf("The given password is correct\n");
1958 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1959}
1960
1961
1962/*********************************************************************************************************************************
1963* The mediumio command *
1964*********************************************************************************************************************************/
1965
1966/**
1967 * Common MediumIO options.
1968 */
1969typedef struct MEDIUMIOCOMMONOPT
1970{
1971 const char *pszFilenameOrUuid;
1972 DeviceType_T enmDeviceType;
1973 const char *pszPasswordFile;
1974} MEDIUMIOCOMMONOPT;
1975typedef MEDIUMIOCOMMONOPT *PMEDIUMIOCOMMONOPT;
1976typedef MEDIUMIOCOMMONOPT const *PCMEDIUMIOCOMMONOPT;
1977
1978/* For RTGETOPTDEF array initializer. */
1979#define MEDIUMIOCOMMONOPT_DEFS() \
1980 { "--disk", 'd', RTGETOPT_REQ_STRING }, \
1981 { "--harddisk", 'd', RTGETOPT_REQ_STRING }, \
1982 { "disk", 'd', RTGETOPT_REQ_STRING }, \
1983 { "harddisk", 'd', RTGETOPT_REQ_STRING }, \
1984 { "--dvd", 'D', RTGETOPT_REQ_STRING }, \
1985 { "--iso", 'D', RTGETOPT_REQ_STRING }, \
1986 { "dvd", 'D', RTGETOPT_REQ_STRING }, \
1987 { "iso", 'D', RTGETOPT_REQ_STRING }, \
1988 { "--floppy", 'f', RTGETOPT_REQ_STRING }, \
1989 { "floppy", 'f', RTGETOPT_REQ_STRING }, \
1990 { "--password-file", 'P', RTGETOPT_REQ_STRING }
1991
1992/* For option switch. */
1993#define MEDIUMIOCOMMONOPT_CASES(a_pCommonOpts) \
1994 case 'd': \
1995 (a_pCommonOpts)->enmDeviceType = DeviceType_HardDisk; \
1996 (a_pCommonOpts)->pszFilenameOrUuid = ValueUnion.psz; \
1997 break; \
1998 case 'D': \
1999 (a_pCommonOpts)->enmDeviceType = DeviceType_DVD; \
2000 (a_pCommonOpts)->pszFilenameOrUuid = ValueUnion.psz; \
2001 break; \
2002 case 'f': \
2003 (a_pCommonOpts)->enmDeviceType = DeviceType_Floppy; \
2004 (a_pCommonOpts)->pszFilenameOrUuid = ValueUnion.psz; \
2005 break; \
2006 case 'P': \
2007 (a_pCommonOpts)->pszPasswordFile = ValueUnion.psz; \
2008 break
2009
2010
2011/**
2012 * Worker for mediumio operations that returns a IMediumIO for the specified
2013 * medium.
2014 *
2015 * @returns Exit code.
2016 * @param pHandler The handler state structure (for IVirtualBox).
2017 * @param pCommonOpts Common mediumio options.
2018 * @param fWritable Whether to open writable (true) or read only
2019 * (false).
2020 * @param rPtrMediumIO Where to return the IMediumIO pointer.
2021 * @param pcbMedium Where to return the meidum size. Optional.
2022 */
2023static RTEXITCODE mediumIOOpenMediumForIO(HandlerArg *pHandler, PCMEDIUMIOCOMMONOPT pCommonOpts, bool fWritable,
2024 ComPtr<IMediumIO> &rPtrMediumIO, uint64_t *pcbMedium = NULL)
2025{
2026 /* Clear returns. */
2027 if (pcbMedium)
2028 *pcbMedium = 0;
2029 rPtrMediumIO.setNull();
2030
2031 /*
2032 * Make sure a medium was specified already.
2033 */
2034 if (pCommonOpts->enmDeviceType == DeviceType_Null)
2035 return errorSyntax("No medium specified!");
2036
2037 /*
2038 * Read the password.
2039 */
2040 Bstr bstrPassword;
2041 if (pCommonOpts->pszPasswordFile)
2042 {
2043 Utf8Str strPassword;
2044 RTEXITCODE rcExit;
2045 if (pCommonOpts->pszPasswordFile[0] == '-' && pCommonOpts->pszPasswordFile[1] == '\0')
2046 rcExit = readPasswordFromConsole(&strPassword, "Enter encryption password:");
2047 else
2048 rcExit = readPasswordFile(pCommonOpts->pszPasswordFile, &strPassword);
2049 if (rcExit != RTEXITCODE_SUCCESS)
2050 return rcExit;
2051 bstrPassword = strPassword;
2052 strPassword.assign(strPassword.length(), '*');
2053 }
2054
2055 /*
2056 * Open the medium and then get I/O access to it.
2057 */
2058 ComPtr<IMedium> ptrMedium;
2059 HRESULT hrc = openMedium(pHandler, pCommonOpts->pszFilenameOrUuid, pCommonOpts->enmDeviceType,
2060 fWritable ? AccessMode_ReadWrite : AccessMode_ReadOnly,
2061 ptrMedium, false /* fForceNewUuidOnOpen */, false /* fSilent */);
2062 if (SUCCEEDED(hrc))
2063 {
2064 CHECK_ERROR2I_STMT(ptrMedium, OpenForIO(fWritable, bstrPassword.raw(), rPtrMediumIO.asOutParam()), hrc = hrcCheck);
2065
2066 /*
2067 * If the size is requested get it after we've opened it.
2068 */
2069 if (pcbMedium && SUCCEEDED(hrc))
2070 {
2071 LONG64 cbLogical = 0;
2072 CHECK_ERROR2I_STMT(ptrMedium, COMGETTER(LogicalSize)(&cbLogical), hrc = hrcCheck);
2073 *pcbMedium = cbLogical;
2074 if (!SUCCEEDED(hrc))
2075 rPtrMediumIO.setNull();
2076 }
2077 }
2078
2079 if (bstrPassword.isNotEmpty())
2080 memset(bstrPassword.mutableRaw(), '*', bstrPassword.length() * sizeof(RTUTF16));
2081 return SUCCEEDED(hrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
2082}
2083
2084
2085/**
2086 * mediumio formatfat
2087 */
2088static RTEXITCODE handleMediumIOFormatFat(HandlerArg *a, int iFirst, PMEDIUMIOCOMMONOPT pCommonOpts)
2089{
2090 /*
2091 * Parse the options.
2092 */
2093 bool fQuick = false;
2094 static const RTGETOPTDEF s_aOptions[] =
2095 {
2096 MEDIUMIOCOMMONOPT_DEFS(),
2097 { "--quick", 'q', RTGETOPT_REQ_NOTHING },
2098 };
2099
2100 RTGETOPTSTATE GetState;
2101 int rc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2102 AssertRC(rc);
2103 RTGETOPTUNION ValueUnion;
2104 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
2105 {
2106 switch (rc)
2107 {
2108 MEDIUMIOCOMMONOPT_CASES(pCommonOpts);
2109
2110 case 'q':
2111 fQuick = true;
2112 break;
2113
2114 default:
2115 return errorGetOpt(rc, &ValueUnion);
2116 }
2117 }
2118
2119 /*
2120 * Open the medium for I/O and format it.
2121 */
2122 ComPtr<IMediumIO> ptrMediumIO;
2123 RTEXITCODE rcExit = mediumIOOpenMediumForIO(a, pCommonOpts, true /*fWritable*/, ptrMediumIO);
2124 if (rcExit != RTEXITCODE_SUCCESS)
2125 return rcExit;
2126 CHECK_ERROR2I_RET(ptrMediumIO, FormatFAT(fQuick), RTEXITCODE_FAILURE);
2127 return RTEXITCODE_SUCCESS;
2128}
2129
2130/**
2131 * mediumio cat
2132 */
2133static RTEXITCODE handleMediumIOCat(HandlerArg *a, int iFirst, PMEDIUMIOCOMMONOPT pCommonOpts)
2134{
2135 /*
2136 * Parse the options.
2137 */
2138 static const RTGETOPTDEF s_aOptions[] =
2139 {
2140 MEDIUMIOCOMMONOPT_DEFS(),
2141 { "--hex", 'H', RTGETOPT_REQ_NOTHING },
2142 { "--offset", 'o', RTGETOPT_REQ_UINT64 },
2143 { "--output", 'O', RTGETOPT_REQ_UINT64 },
2144 { "--size", 's', RTGETOPT_REQ_UINT64 },
2145 };
2146 bool fHex = false;
2147 uint64_t off = 0;
2148 const char *pszOutput = NULL;
2149 uint64_t cb = UINT64_MAX;
2150
2151 RTGETOPTSTATE GetState;
2152 int rc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), iFirst, 0);
2153 AssertRC(rc);
2154 RTGETOPTUNION ValueUnion;
2155 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
2156 {
2157 switch (rc)
2158 {
2159 MEDIUMIOCOMMONOPT_CASES(pCommonOpts);
2160
2161 case 'H':
2162 fHex = true;
2163 break;
2164
2165 case 'o':
2166 off = ValueUnion.u64;
2167 break;
2168
2169 case 'O':
2170 pszOutput = ValueUnion.psz;
2171 break;
2172
2173 case 's':
2174 cb = ValueUnion.u64;
2175 break;
2176
2177 default:
2178 return errorGetOpt(rc, &ValueUnion);
2179 }
2180 }
2181
2182 /*
2183 * Open the medium for I/O.
2184 */
2185 ComPtr<IMediumIO> ptrMediumIO;
2186 uint64_t cbMedium;
2187 RTEXITCODE rcExit = mediumIOOpenMediumForIO(a, pCommonOpts, false /*fWritable*/, ptrMediumIO, &cbMedium);
2188 if (rcExit == RTEXITCODE_SUCCESS)
2189 {
2190 /*
2191 * Do we have an output file or do we write to stdout?
2192 */
2193 PRTSTREAM pOut = NULL;
2194 if (pszOutput && (pszOutput[0] != '-' || pszOutput[1] != '\0'))
2195 {
2196 int vrc = RTStrmOpen(pszOutput, fHex ? "wt" : "wb", &pOut);
2197 if (RT_FAILURE(vrc))
2198 rcExit = RTMsgErrorExitFailure("Error opening '%s' for writing: %Rrc", pszOutput, vrc);
2199 }
2200 else
2201 {
2202 pOut = g_pStdOut;
2203 if (!fHex)
2204 RTStrmSetMode(pOut, true, -1);
2205 }
2206
2207 if (rcExit == RTEXITCODE_SUCCESS)
2208 {
2209 /*
2210 * Adjust 'cb' now that we've got the medium size.
2211 */
2212 if (off >= cbMedium)
2213 {
2214 RTMsgWarning("Specified offset (%#RX64) is beyond the end of the medium (%#RX64)", off, cbMedium);
2215 cb = 0;
2216 }
2217 else if ( cb > cbMedium
2218 || cb + off > cbMedium)
2219 cb = cbMedium - off;
2220
2221 /*
2222 * Hex dump preps. (The duplication detection is making ASSUMPTIONS about
2223 * all the reads being a multiple of cchWidth, except for the final one.)
2224 */
2225 char abHexBuf[16] = { 0 };
2226 size_t cbHexBuf = 0;
2227 unsigned const cchWidth = RT_ELEMENTS(abHexBuf);
2228 uint64_t const offEndDupCheck = cb - cchWidth;
2229 uint64_t cDuplicates = 0;
2230
2231 /*
2232 * Do the reading.
2233 */
2234 while (cb > 0)
2235 {
2236 char szLine[32 + cchWidth * 4 + 32];
2237
2238 /* Do the reading. */
2239 uint32_t const cbToRead = (uint32_t)RT_MIN(cb, _128K);
2240 SafeArray<BYTE> SafeArrayBuf;
2241 HRESULT hrc = ptrMediumIO->Read(off, cbToRead, ComSafeArrayAsOutParam(SafeArrayBuf));
2242 if (FAILED(hrc))
2243 {
2244 RTStrPrintf(szLine, sizeof(szLine), "Read(%zu bytes at %#RX64)", cbToRead, off);
2245 com::GlueHandleComError(ptrMediumIO, szLine, hrc, __FILE__, __LINE__);
2246 break;
2247 }
2248
2249 /* Output the data. */
2250 size_t const cbReturned = SafeArrayBuf.size();
2251 if (cbReturned)
2252 {
2253 BYTE const *pbBuf = SafeArrayBuf.raw();
2254 int vrc = VINF_SUCCESS;
2255 if (!fHex)
2256 vrc = RTStrmWrite(pOut, pbBuf, cbReturned);
2257 else
2258 {
2259 /* hexdump -C */
2260 uint64_t offHex = off;
2261 uint64_t const offHexEnd = off + cbReturned;
2262 while (offHex < offHexEnd)
2263 {
2264 if ( offHex >= offEndDupCheck
2265 || cbHexBuf == 0
2266 || memcmp(pbBuf, abHexBuf, cchWidth) != 0
2267 || ( cDuplicates == 0
2268 && ( offHex + cchWidth >= offEndDupCheck
2269 || memcmp(pbBuf + cchWidth, pbBuf, cchWidth) != 0)) )
2270 {
2271 if (cDuplicates > 0)
2272 {
2273 RTStrmPrintf(pOut, "********** <ditto x %RU64>\n", cDuplicates);
2274 cDuplicates = 0;
2275 }
2276
2277 size_t cch = RTStrPrintf(szLine, sizeof(szLine), "%012RX64:", offHex);
2278 unsigned i;
2279 for (i = 0; i < cchWidth && offHex + i < offHexEnd; i++)
2280 {
2281 static const char s_szHexDigits[17] = "0123456789abcdef";
2282 szLine[cch++] = (i & 7) || i == 0 ? ' ' : '-';
2283 uint8_t const u8 = pbBuf[i];
2284 szLine[cch++] = s_szHexDigits[u8 >> 4];
2285 szLine[cch++] = s_szHexDigits[u8 & 0xf];
2286 }
2287 while (i++ < cchWidth)
2288 {
2289 szLine[cch++] = ' ';
2290 szLine[cch++] = ' ';
2291 szLine[cch++] = ' ';
2292 }
2293 szLine[cch++] = ' ';
2294
2295 for (i = 0; i < cchWidth && offHex + i < offHexEnd; i++)
2296 {
2297 uint8_t const u8 = pbBuf[i];
2298 szLine[cch++] = u8 < 127 && u8 >= 32 ? u8 : '.';
2299 }
2300 szLine[cch++] = '\n';
2301 szLine[cch] = '\0';
2302
2303 vrc = RTStrmWrite(pOut, szLine, cch);
2304 if (RT_FAILURE(vrc))
2305 break;
2306
2307
2308 /* copy bytes over to the duplication detection buffer. */
2309 cbHexBuf = (size_t)RT_MIN(cchWidth, offHexEnd - offHex);
2310 memcpy(abHexBuf, pbBuf, cbHexBuf);
2311 }
2312 else
2313 cDuplicates++;
2314
2315 /* Advance to next line. */
2316 pbBuf += cchWidth;
2317 offHex += cchWidth;
2318 }
2319 }
2320 if (RT_FAILURE(vrc))
2321 {
2322 rcExit = RTMsgErrorExitFailure("Error writing to '%s': %Rrc", pszOutput, vrc);
2323 break;
2324 }
2325 }
2326
2327 /* Advance. */
2328 if (cbReturned != cbToRead)
2329 {
2330 rcExit = RTMsgErrorExitFailure("Expected read() at offset %RU64 (%#RX64) to return %#zx bytes, only got %#zx!\n",
2331 off, off, cbReturned, cbToRead);
2332 break;
2333 }
2334 off += cbReturned;
2335 cb -= cbReturned;
2336 }
2337
2338 /*
2339 * Close output.
2340 */
2341 if (pOut != g_pStdOut)
2342 {
2343 int vrc = RTStrmClose(pOut);
2344 if (RT_FAILURE(vrc))
2345 rcExit = RTMsgErrorExitFailure("Error closing '%s': %Rrc", pszOutput, vrc);
2346 }
2347 else if (!fHex)
2348 RTStrmSetMode(pOut, false, -1);
2349 }
2350 }
2351 return rcExit;
2352}
2353
2354
2355RTEXITCODE handleMediumIO(HandlerArg *a)
2356{
2357 /*
2358 * Parse image-option and sub-command.
2359 */
2360 static const RTGETOPTDEF s_aOptions[] =
2361 {
2362 MEDIUMIOCOMMONOPT_DEFS(),
2363 /* sub-commands */
2364 { "formatfat", 1000, RTGETOPT_REQ_NOTHING },
2365 { "cat", 1001, RTGETOPT_REQ_NOTHING },
2366 };
2367 MEDIUMIOCOMMONOPT CommonOpts = { NULL, DeviceType_Null, NULL };
2368
2369 RTGETOPTSTATE GetState;
2370 int rc = RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
2371 AssertRC(rc);
2372 RTGETOPTUNION ValueUnion;
2373 while ((rc = RTGetOpt(&GetState, &ValueUnion)) != 0)
2374 {
2375 switch (rc)
2376 {
2377 MEDIUMIOCOMMONOPT_CASES(&CommonOpts);
2378
2379 /* Sub-commands: */
2380 case 1000:
2381 setCurrentSubcommand(HELP_SCOPE_MEDIUMIO_FORMATFAT);
2382 return handleMediumIOFormatFat(a, GetState.iNext, &CommonOpts);
2383 case 1001:
2384 setCurrentSubcommand(HELP_SCOPE_MEDIUMIO_CAT);
2385 return handleMediumIOCat(a, GetState.iNext, &CommonOpts);
2386
2387 case VINF_GETOPT_NOT_OPTION:
2388 return errorUnknownSubcommand(ValueUnion.psz);
2389
2390 default:
2391 return errorGetOpt(rc, &ValueUnion);
2392 }
2393 }
2394 return errorNoSubcommand();
2395}
2396
2397#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