VirtualBox

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

Last change on this file since 90739 was 87435, checked in by vboxsync, 4 years ago

Main: bugref:9224: Added error reporting for invalid property during medium creation

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

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